////////////////////////////////////////////////////////////////////////////
//
// File: bot.cc
//
// Description:
//   This is the implementation file for the bot class.  It handles all
//   bot-related functionality.  (Including the sqlite database stuff).
//
// Database info:
//   There are 3 main tables.  The first table is the 'users' table.
//   These are the parameters and variables used to create the table.
//   Here 'user' is the user name in all lowercase letters.  Also, 'time' 
//   is the last time that the user was seen in the channel by the bot.  
//   Note that the number(1)'s are just used as a boolean value,
//   0 == false, 1 == true.
//
//     create table users (user varchar(32), 
// 			time number(10), 
// 			banned number(1), 
// 			guest number(1), 
// 			regular number(1), 
// 			operator number(1), 
// 			administrator number(1), 
// 			havemail number(1));
//
//   The table 'botmails' contains all the mail from and to a user and
//   the time that the mail was created. + the message.
//
//        create table botmails (touser varchar(32), 
// 			      fromuser varchar(32), 
// 			      time number(10), 
// 			      message varchar(255));
//
//   The table 'allseens' contains the last 20 users seen in the channel.
//   From the names here, we reference the 'users' table to see what time
//   the user was last seen.  Note user0 is the most recently seen user.
//
//         create table allseens (user0 varchar(32),
// 		                  user1 varchar(32),
// 		                  user2 varchar(32),
// 		                  ...
// 		                  user19 varchar(32));
//
//////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <ctime>   //  for time_t and timestamps
#include <string>
#include <sstream> // parsing and manipulation of strings.

#include <sqlite.h>  // for our SQL database.

//#include <stdio.h>  // for sleep.
//#include <sys/poll.h>
//#include<iostream>
//#include <sys/types.h>
//#include <unistd.h>

//#include <assert.h>  // for assert()
using namespace std;

// My stuff:
#include "bot.h"
#include "../conGuy/types.h"
#include "../conGuy/conGuy.h"
#include "../parser/parser.h"


#define BOT_CONFIG_FILE "config/bot.cfg"  // Default connect config file.
#define BOT_SQLITE_DB "databases/database.db"  // Default database file.

// extern to allow for debug logging.
extern QTextStream *Debug;

//////////////////////////////////////////////////////////////////////////////
// 
// Function:  bot::bot()
// 
// Description:  This is the constructor for the bot class.
//
bot::bot( QObject *parent, const char* name )
    : 
    QObject( parent, name )
{
  // Allocate memory for the conGuys.
  bnet = 0;
  useast = new conGuy;
  uswest = new conGuy;
  europe = new conGuy;
  asia   = new conGuy;
  
  // We default to tell main not to re-execute the bot.
  // This variable is used in the case of a !reconnect command.
  reExecFlag = false;

  readyToContinueStartup_ = false;

  // Initialize the random generator for cointoss.
  srand( time(0) );

  connect( useast, SIGNAL(newEvent(conGuy*,Event)),
           this, SLOT(handleChatEvent(conGuy*,Event)) );
  connect( uswest, SIGNAL(newEvent(conGuy*,Event)),
           this, SLOT(handleChatEvent(conGuy*,Event)) );
  connect( europe, SIGNAL(newEvent(conGuy*,Event)),
           this, SLOT(handleChatEvent(conGuy*,Event)) );
  connect( asia, SIGNAL(newEvent(conGuy*,Event)),
           this, SLOT(handleChatEvent(conGuy*,Event)) );

  connect( useast, SIGNAL(connectedToBnet()),
           this, SLOT(readyToContinueStartup()) );
  connect( uswest, SIGNAL(connectedToBnet()),
           this, SLOT(readyToContinueStartup()) );
  connect( europe, SIGNAL(connectedToBnet()),
           this, SLOT(readyToContinueStartup()) );
  connect( asia, SIGNAL(connectedToBnet()),
           this, SLOT(readyToContinueStartup()) );

}

//////////////////////////////////////////////////////////////////////////////
// 
// Function:  bot::~bot()
// 
// Description:  This is the destructor for the bot class.
//
bot::~bot() {
    // Close the sqlite database.
    sqlite_close(db);

  // Deallocate memory.
  delete useast;
  delete uswest;
  delete europe;
  delete asia;
  bnet   = 0;
  useast = 0;
  uswest = 0;
  europe = 0;
  asia   = 0;
}


//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION:  bool bot::exec(bool &reExec)
//
// DESCRIPTION: 
//   This function is what is called to execute the bot.
//   It opens the config files, logs stuff, connects, and runs.  Then it
//   disconnects at the end when done.
//
// PARAMETERS:
//   bool &reExec
//     This variable will tell int main() if the bot should be re-executed.  It
//     is basically just a way to pass the reExec flag back to main.  The 
//     only time reExec will be true when the function returns is if the
//     admin command !reconnect was used.  
//
// RETURNS:  bool for success or failure.
//
// NOTES:  None.
//
void bot::run() {
  string logfilename = string(LOGFILE_BASE) + 
                       string("bot.log");
  // For timestamp.
  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  reExecFlag = false;  // Set the flag to tell main not to execute this 
                       // function after it returns.

  // Reset this flag.
  readyToContinueStartup_ = false;

  // If the log file is open, close it.
  if(botlog.is_open() == true)
    botlog.close();

  // Open the logfile for appending.
  botlog.open(logfilename.c_str(),
	       ios_base::out | ios_base::app);

  // Log timestamp of connection start.
  botlog  << endl
	  << "---------------------------------------------------------------"
	  << endl << "GiTMbot started at:                   "
	  << string(asctime(timeinfo))
	  << "---------------------------------------------------------------"
	  << endl;

  
  botlog << "Reading in bot configuration file " << BOT_CONFIG_FILE
	    << " ...";
  if(!read_config()) {
      return;
  }
  
  botlog << "done." << endl
	 << "Now attempting to connect to the chat gateways." << endl;

  if(!(useast->connectNow("useast"))) {
    *Debug << "ERROR:  connectNow() failed for useast gateway" << endl;
    botlog << "The useast gateway logon failed.  Check log files for why."
	     << endl; 
  }
  if(!(uswest->connectNow("uswest"))) {
    *Debug << "ERROR:  connectNow() failed for uswest gateway" << endl;
    botlog << "The uswest gateway logon failed.  Check log files for why."
	     << endl; 
  }
  if(!(europe->connectNow("europe"))) {
    *Debug << "ERROR:  connectNow() failed for europe gateway" << endl;
    botlog << "The europe gateway logon failed.  Check log files for why."
	     << endl; 
  }
  if(!(asia->connectNow("asia"))) {
    *Debug << "ERROR:  connectNow() failed for asia gateway" << endl;
    botlog << "The asia gateway logon failed.  Check log files for why."
	     << endl; 
  }


  while( this->readyToContinueStartup_ == false )
  {
      *Debug << "DEBUG:  bot.cpp:  Waiting for completed connecting for "
             << "at least one conGuy." << endl;
      this->sleep( 1 );
  }
  // Sleep one more second, just in case.
  this->sleep( 1 );


//   // Sleep for a few seconds.
//   this->sleep( 5 );

  // Set bnet.
  bnet = 0;
  if(useast->isConnected() && useast->getMainGateway() == "useast") {
    bnet = useast;
  }
  else if(uswest->isConnected() && uswest->getMainGateway() == "uswest") {
    bnet = uswest;
  }
  else if(europe->isConnected() && europe->getMainGateway() == "europe") {
    bnet = europe;
  }
  else if(asia->isConnected() && asia->getMainGateway() == "asia") {
    bnet = asia;
  }

  
  // If bnet is still == 0 then either
  // none of the bots connected or the conGuy for the main gateway
  // our bot will be on isn't connected.

  if(bnet == 0) {
    *Debug << "ERROR:  The gateway your ops bot is gonna run on isn't logged"
	   << " in!" << endl;
    return;
  }



  // Set that this conGuy is the bnet conguy.
  bnet->setBnetConGuy( true );
  unique_username = bnet->username();


  // Open the sql database
  *Debug << "About to open the db." << endl;
  if(! open_sql_db()) 
  {
      *Debug << "Open failed." << endl;
      return;
  }
  else 
  {
      *Debug << "Open successful." << endl;
  }

  if(! promoteUserToAdmin(admin_accnt))
  {
      *Debug << "Failed to promote admin account user to admin." << endl;
      return;
  }


  botlog << "Logged in as user: " << unique_username << endl;
  botlog << "Bot started! " << endl;

  // Open chat logs.
  openChatLogs();

  // Spawn threads for each connected conGuy and wait till
  //  'bnet' conGuy is done.
  spawn_threads_and_wait();


  // All done!
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  botlog  << "GiTMbot disconnect at:                 "
	  << string(asctime(timeinfo))
	  << "---------------------------------------------------------------"
	  << endl;

  botlog.close();

  // TODO:  do i need to terminate the qthreads?

  // Disconnect.
  if(useast->isConnected())
  {
//    useast->terminate();
    useast->disconnectNow();
  }

  if(uswest->isConnected())
  {
//    uswest->terminate();
    uswest->disconnectNow();
  }
  if(europe->isConnected())
  {
//    europe->terminate();
    europe->disconnectNow();
  }

  if(asia->isConnected())
  {
//    asia->terminate();
    asia->disconnectNow();
  }


} // End of     bool bot::run(bool &reExec)

bool bot::getReExec() const
{
    return reExecFlag;
}

//////////////////////////////////////////////////////////////////////////////
// 
// FUNCTION:  bool bot::read_config()
//
// DESCRIPTION:
//   This function reads in the config settings from the config file.  
//   It sets the variables according to what the parser gets for it.
//
// RETURN VALUE: 
//   bool for success/failure.
//
// TODO:  Just make a single config option for multi-battle.net bot support.
//    so that there is just one main bot on one single server.
//    (it would just disable the eastw ... and whereis commands.
bool bot::read_config() {
// These are the configured options from config file.
//   string database_file;
//   string admin_accnt;
//   string chatlog_file_base;
//   bool bot_cmds;
//   bool bot_mail;
//   bool bot_mailops;
//   bool bot_mailall;
//   bool bot_allseen;
//   bool bot_seen;
//   bool bot_banlist;
//   bool bot_beep;
//   bool bot_admin;
//   bool bot_greet;
//   bool bot_say;
//   bool bot_say_exact;
//   bool bot_smackdown;
//   bool bot_mama_smackdown;
//   bool bot_polls;  // polls and votes
  
  // Create a parser object and open the bot config file.
  parser parserGuy(string(BOT_CONFIG_FILE));

  // Start retrieveing variables.

  // Fetch database_file
  if(!parserGuy.fetch("database_file", database_file)) {
    *Debug << "ERROR:  Couldn't get database_file from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get database_file from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file database_file: " << database_file
	 << endl;


  // Fetch admin_accnt
  if(!parserGuy.fetch("admin_accnt", admin_accnt)) {
    *Debug << "ERROR:  Couldn't get admin_accnt from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get admin_accnt from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file admin_accnt: " << admin_accnt
	 << endl;


  // Fetch chatlog_file_base
  if(!parserGuy.fetch("chatlog_file_base", chatlog_file_base)) {
    *Debug << "ERROR:  Couldn't get chatlog_file_base from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get chatlog_file_base from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file chatlog_file_base: " 
	 << chatlog_file_base
	 << endl;



  // Fetch bot_cmds
  if(!parserGuy.fetch("bot_cmds", bot_cmds)) {
    *Debug << "ERROR:  Couldn't get bot_cmds from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_cmds from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_cmds: " 
	 << bot_cmds
	 << endl;



  // Fetch bot_mail
  if(!parserGuy.fetch("bot_mail", bot_mail)) {
    *Debug << "ERROR:  Couldn't get bot_mail from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_mail from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_mail: " 
	 << bot_mail
	 << endl;



  // Fetch bot_mailops
  if(!parserGuy.fetch("bot_mailops", bot_mailops)) {
    *Debug << "ERROR:  Couldn't get bot_mailops from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_mailops from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_mailops: " 
	 << bot_mailops
	 << endl;




  // Fetch bot_mailall
  if(!parserGuy.fetch("bot_mailall", bot_mailall)) {
    *Debug << "ERROR:  Couldn't get bot_mailall from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_mailall from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_mailall: " 
	 << bot_mailall
	 << endl;




  // Fetch bot_allseen
  if(!parserGuy.fetch("bot_allseen", bot_allseen)) {
    *Debug << "ERROR:  Couldn't get bot_allseen from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_allseen from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_allseen: " 
	 << bot_allseen
	 << endl;



  // Fetch bot_seen
  if(!parserGuy.fetch("bot_seen", bot_seen)) {
    *Debug << "ERROR:  Couldn't get bot_seen from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_seen from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_seen: " 
	 << bot_seen
	 << endl;

  

  // Fetch bot_banlist
  if(!parserGuy.fetch("bot_banlist", bot_banlist)) {
    *Debug << "ERROR:  Couldn't get bot_banlist from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_banlist from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_banlist: " 
	 << bot_banlist
	 << endl;



  // Fetch bot_beep
  if(!parserGuy.fetch("bot_beep", bot_beep)) {
    *Debug << "ERROR:  Couldn't get bot_beep from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_beep from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_beep: " 
	 << bot_beep
	 << endl;



  // Fetch bot_admin
  if(!parserGuy.fetch("bot_admin", bot_admin)) {
    *Debug << "ERROR:  Couldn't get bot_admin from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_admin from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_admin: " 
	 << bot_admin
	 << endl;


  // Fetch bot_greet
  if(!parserGuy.fetch("bot_greet", bot_greet)) {
    *Debug << "ERROR:  Couldn't get bot_greet from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_greet from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_greet: " 
	 << bot_greet
	 << endl;



  // Fetch bot_say
  if(!parserGuy.fetch("bot_say", bot_say)) {
    *Debug << "ERROR:  Couldn't get bot_say from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_say from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_say: " 
	 << bot_say
	 << endl;


  // Fetch bot_say_exact
  if(!parserGuy.fetch("bot_say_exact", bot_say_exact)) {
    *Debug << "ERROR:  Couldn't get bot_say_exact from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_say_exact from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_say_exact: " 
	 << bot_say_exact
	 << endl;


  // Fetch bot_smackdown
  if(!parserGuy.fetch("bot_smackdown", bot_smackdown)) {
    *Debug << "ERROR:  Couldn't get bot_smackdown from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_smackdown from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_smackdown: " 
	 << bot_smackdown
	 << endl;


  // Fetch bot_mama_smackdown
  if(!parserGuy.fetch("bot_mama_smackdown", bot_mama_smackdown)) {
    *Debug << "ERROR:  Couldn't get bot_mama_smackdown from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_mama_smackdown from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_mama_smackdown: " 
	 << bot_mama_smackdown
	 << endl;


  // Fetch bot_mama_smackdown
  if(!parserGuy.fetch("bot_mama_smackdown", bot_mama_smackdown)) {
    *Debug << "ERROR:  Couldn't get bot_mama_smackdown from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_mama_smackdown from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }
  *Debug << "DEBUG:  Read in from cfg file bot_mama_smackdown: " 
	 << bot_mama_smackdown
	 << endl;


  // Fetch bot_polls
  if(!parserGuy.fetch("bot_polls", bot_polls)) {
    *Debug << "ERROR:  Couldn't get bot_polls from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_polls from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }

  if(!parserGuy.fetch("bot_chatlog", bot_chatlog)) {
    *Debug << "ERROR:  Couldn't get bot_chatlog from the parser."
	   << "  (check cfg file?)" << endl
	   << "                   " << __FILE__ << "(" << __LINE__ << ")"
	   << endl;
    botlog << "ERROR:  Couldn't get bot_polls from config file."
	     << " (check bot.cfg file?)" << endl;
    return false;
  }

  *Debug << "DEBUG:  Read in from cfg file bot_polls: " 
	 << bot_polls
	 << endl;


  return true;
}  // End of    bool bot::read_config()



//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION:  bool bot::spawn_threads_and_wait()
// 
// DESCRIPTION:  
// This function spawns threads for chat event handlers and waits
// untill the 'bnet' conGuy finishes.  
//
// RETURNS:
//   bool for success/failure of our bnet (bot) conGuy.
//
// NOTES: 
//   See the humongous comment within code for other important
//   functionality details.
//
bool bot::spawn_threads_and_wait() {
  // Start the conGuy threads.
  botlog << "DEBUG:  Starting useast thread..." << endl;
  useast->start();
  botlog << "DEBUG:  Starting useast thread complete." << endl;
  botlog << "DEBUG:  Starting uswest thread..." << endl;
  uswest->start();
  botlog << "DEBUG:  Starting uswest thread complete." << endl;
  botlog << "DEBUG:  Starting europe thread..." << endl;
  europe->start();
  botlog << "DEBUG:  Starting europe thread complete." << endl;
  botlog << "DEBUG:  Starting asia thread..." << endl;
  asia->start();
  botlog << "DEBUG:  Starting asia thread complete." << endl;
    
  botlog << "DEBUG:  Completed starting all conGuy threads." << endl;
  
  botlog << "DEBUG:  Now waiting for the bnet conGuy to finish..." << endl;

  // Join on the bnet conGuy, whichever he may be.
  bnet->wait();

  // Ok, if it got down here, we completed the thread for bnet conGuy.
  // 
  // status is an int for the return status.  
  // in the thread, the thread returns by doing pthread_exit((void *) 0);
  botlog << "DEBUG:  Our ops bot thread completed." << endl;
  
  return true;
}  // End of bool bot::spawn_threads_and_wait()







//////////////////////////////////////////////////////////////////////////////
// 
// FUNCTION:  string bot::get_timestamp() 
// 
// DESCRIPTION:
// Return the current time in this form:   [4/18 17:38:04]
// for april 18th, at 5:38:04 pm.
//
// RETURN VALUE:  
//   string
//     This timestamp returned is in string form.
//
string bot::get_timestamp() {
  string rv;
  char currTime[64];

  time_t rawtime;
  struct tm * timeinfo;

  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  sprintf(currTime, "[%d/%d %02d:%02d:%02d]",
	  timeinfo->tm_mon+1, 
	  timeinfo->tm_mday,
	  timeinfo->tm_hour,
	  timeinfo->tm_min,
	  timeinfo->tm_sec);
	  
  rv = string(currTime);
  return rv;
} // End of   string bot::get_timestamp() 

// Does nothing right now.
void bot::openChatLogs()
{
}


// Slot that gets called every time a event is received.
void bot::handleChatEvent( conGuy* myConGuy, Event new_event )
{
    //(*Debug) << "Inside bot::handleChatEvent() from gateway "
    //    << myConGuy->getGateway() << endl;
    
  // exit_now is set in conGuy if some error occurred and we should exit.
  // done is set through a ! triggered command by a user.
  if(new_event.exit_now == false && myConGuy->done == false) {

    // If the conGuy isn't connected, flush send and request queues.
    //   Also, sleep for a few seconds.  Hopefully by doing this, 
    //   the CPU won't stress too much.
    if(myConGuy->isConnected() == false) {
      myConGuy->purgeQueues();
      // sleep(5);
    }

    // try_again == false means handle this event.
    // try_again == true means don't handle this event and try to get 
    //   another event.
    if(new_event.try_again == false) {

      // Log the event.
      switch(new_event.eventID) {
      case EID_SHOWUSER:
           	(*Debug) << get_timestamp() << " User in channel: " 
           		 << new_event.username << endl;
	break;
      case EID_JOIN:
           	(*Debug) << get_timestamp() << " User joined channel: "
           		 << new_event.username << endl;
	if(bnet == myConGuy) {
	  if(userIsBanned(new_event.username)) {
	    // The user is banned and he tried to enter our channel.
	    //   Ban him again!
	    Event fake_event;
	    fake_event.username = myConGuy->username();
	    cmd_ban(fake_event, myConGuy, new_event.username, 
		    string("Dude, someone doesn't like you.  You're ") +
		    string("banned like whoa."));
	  }
	  else {
	    // Good user...
	    update_user(new_event.username);   // Update timestamps and such.
	    if(has_mail(new_event.username)) {
	      // Let the user know he has mail.
	      sendQueue *toSend;
	      toSend = myConGuy->getSendQueue();
	      (toSend->mutex).lock();
	      toSend->out.push_back( string("/w ") + new_event.username +
				     string(" You have botmail.  Type ") +
				     string("!inbox to check it."));
	      (toSend->mutex).unlock();
	    }
	  }
	}
	break;
      case EID_LEAVE:
           	(*Debug) << get_timestamp() << " User left channel: " 
           		 << new_event.username << endl;
	break;
      case EID_WHISPER:
           	(*Debug) << get_timestamp() << " Whisper from <" 
                      << new_event.username
           		 << "> " << new_event.text << endl;
	parse_for_commands(new_event, myConGuy);
	break;
      case EID_TALK:
           	(*Debug) << get_timestamp() << " <" << new_event.username 
                      << "> "
           		 << new_event.text << endl;
	if(bnet == myConGuy) {
	  update_user(new_event.username);
	}
	parse_for_commands(new_event, myConGuy);
	break;
      case EID_BROADCAST:
           	(*Debug) << get_timestamp() << " Server broadcast: "
           		<< new_event.username << " " << new_event.text << endl;
	break;
      case EID_CHANNEL:
           	(*Debug) << get_timestamp() << " Channel:  " << new_event.text 
                      << endl;
	break;
      case EID_USERFLAGS:
	// We only care if we have the ops or flag change.
	if(new_event.username == unique_username) {
	  if(new_event.flags == USER_CHANNELOP) {
               (*Debug) << get_timestamp() << " We have channel ops." << endl;
	    myConGuy->setHaveOps(true);
	  }
	  else {
               (*Debug) << get_timestamp() << " We don't have channel ops."
                       << endl;
	    myConGuy->setHaveOps(false);
	  }
	}
	break;
      case EID_WHISPERSENT:
           (*Debug) << get_timestamp() << " Whisper sent <" 
            << new_event.username << "> " << new_event.text << endl;
	parse_for_request_status(new_event, myConGuy);
	break;
      case EID_CHANNELFULL:
           	(*Debug) << get_timestamp() << " Channel Full." << endl;
	break;
      case EID_CHANNELDOESNOTEXIST:
           	(*Debug) << get_timestamp() << " Channel does not exist." 
                      << endl;
	break;
      case EID_CHANNELRESTRICTED:
           	(*Debug) << get_timestamp() << " Channel is restricted." 
                      << endl;
	break;
      case EID_INFO:
           	(*Debug) << get_timestamp() << " Info: " 
                        << new_event.username
           		 << " " << new_event.text << endl;
          parse_for_request_status(new_event, myConGuy);
	break;
      case EID_ERROR: 
           	(*Debug) << get_timestamp() << " Error: " << new_event.username
           		 << " " << new_event.text << endl;
	parse_for_request_status(new_event, myConGuy);
	break;
      case EID_EMOTE:
           	(*Debug) << get_timestamp() << " Emote: " 
                        << new_event.username 
           		 << "    " << new_event.text << endl;
	break;
      default:
           	(*Debug) << get_timestamp() << " Unknown event ID." << endl;
	break;
      }
    } // end of     if(new_event.try_again == false)
  }
}

// Slot that gets called when a bot connects successfully.  This thing
// kinda sucks because it gets called for every conGuy... not just the
// bnet conGuy as we would like.  The design of this whole project
// really sucks... but I suppose it aint bad for doing it without any
// college-level instruction on C++.
void bot::readyToContinueStartup()
{
    readyToContinueStartup_ = true;
}


//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION:  bool bot::parse_for_request_status(event ev, conGuy* myConGuy)
//
// DESCRIPTION:
//   Parsing for a bot comamnd.  In the form "!command-name-here ..."
//
// RETURNS:
//  bool for if it successfully parsed for a request.
//
bool bot::parse_for_request_status(Event ev, conGuy* myConGuy) {
  unsigned int i;
  string waiting;
  conGuy* returnToThisGuy;
  string returnToThisUser;
  sendRequests *requests;
  sendQueue    *toSend;

  // Get the requests for myConGuy and get a lock on the mutex.
  requests = myConGuy->getSendRequests();
  (requests->mutex).lock();
  
  if(requests->waiting.size() == 0) {
    // We have no requests waiting, we're done.
    (requests->mutex).unlock();
    return true;
  }
  else {
    *Debug << "about to check the requests.  requests->waiting.size() == "
	   << requests->waiting.size() << endl;
  }

  // Ok, we know we have at least 1 request pending.
  // Grab the values we're interested in.
  waiting          = requests->waiting.front();
  returnToThisGuy  = requests->returnToConGuy.front();
  returnToThisUser = requests->returnToWho.front();
  
  // Confirm a match in event and request.
  switch(ev.eventID) {
  case EID_WHISPERSENT: {
    //  Break the waiting string up into words so we can analyze the 
    //    username part.
    string thecommand;
    string theuser;
    string themessage;
    unsigned int message_start_pos = 0;
    
    // Let's first extract the line sent into a string.
    istringstream waitingLine(waiting.c_str());
    waitingLine >> thecommand;  // our /w command.
    waitingLine >> theuser;     // Grab the first argument.
    waitingLine >> themessage;  // Grab the first word of the message.
    
    message_start_pos = waiting.find(themessage.c_str(), 0);
    if(message_start_pos == 0 || message_start_pos == string::npos) {
      // Crap!
      *Debug << "ERROR:  message_start_pos == 0 or it == string::npos" << endl;
      (requests->mutex).unlock();
      return false;
    }
    themessage = waiting.substr( message_start_pos );

    // Ok, done with that.  Now let's make the user strings theuser and 
    //   ev.username to be lowercase.

    for(i = 0; i < theuser.size(); i++)
      theuser[i] = tolower(theuser[i]);
    for(i = 0; i < ev.username.size(); i++)
      ev.username[i] = tolower(ev.username[i]);

    // transform function doesn't work with g++?

    //     std::transform (theuser.begin(), theuser.end(), // source
    // 		    theuser.begin(),                // destination
    // 		    std::tolower);                  // operation
    //     std::transform (ev.username.begin(), ev.username.end()
    // 		    ev.username.begin()
    // 		    std::tolower);
    

    //    *Debug << "DEBUG:  Bot got ! waitinguser == " << theuser << endl;
      
    if((thecommand   + string(" ") + theuser     + string(" ") + themessage) ==
       (string("/w") + string(" ") + ev.username + string(" ") + ev.text)) {
      // Match!
      *Debug << "request EID_WHISPERSENT match!" << endl;
      // Pop off each list and unlock the mutex.
      requests->waiting.pop_front();
      requests->returnToConGuy.pop_front();
      requests->returnToWho.pop_front();
      *Debug << "popped off waiting list.  request->waiting.size() == "
	     << requests->waiting.size() << endl;
      (requests->mutex).unlock();

      // Let the user that requested know it was sent.
      toSend = returnToThisGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( "/w "            + returnToThisUser + " " +
			     "Whisper sent: " + ev.text );
      (toSend->mutex).unlock();
    }
    else {
      // No match.
      *Debug << "no match for whispersent" << endl;
      (requests->mutex).unlock();
    }
  } // end case EID_WHISPERSENT
    break;
  case EID_INFO: {
    unsigned int i = 0;
    if(waiting.substr(0, string("/whereis").size()) == "/whereis") {
      string dummy;         // 1st word in the waiting string.
      string theuser;       // 2nd word in the waiting string.
      string word1, word2;  // 1st and 2nd words in the event.text string
      string word3;
      // Get theuser.

      // Let's first extract the line sent into a string.
      istringstream waitingLine(waiting.c_str());
      waitingLine >> dummy;  // throw away the command.
      waitingLine >> theuser;   // Grab the first argument.

      *Debug << "DEBUG:  Bot got ! theuser == " << theuser << endl;
      
      // Get the first and second word in the event text we got back.
      istringstream eventLine(ev.text.c_str());
      eventLine >> word1;
      eventLine >> word2;
      eventLine >> word3;

      // Let's convert the comparison strings to lowercase first.
      for(i = 0; i < theuser.size(); i++)
	theuser[i] = tolower(theuser[i]);
      for(i = 0; i < word1.size(); i++)
	word1[i] = tolower(word1[i]);
      for(i = 0; i < word2.size(); i++)
	word2[i] = tolower(word2[i]);

      if(word1 == theuser && word2 == "is") {
	// Match!  Found the user the person was looking for.
	// Pop off each list and unlock the mutex.
	*Debug << "request EID_INFO match!" << endl;
	requests->waiting.pop_front();
	requests->returnToConGuy.pop_front();
	requests->returnToWho.pop_front();
	*Debug << "popped off waiting list.  request->waiting.size() == "
	       << requests->waiting.size() << endl;
	(requests->mutex).unlock();
	
	// Let the user that requested know.
	toSend = returnToThisGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( "/w " + returnToThisUser + " " +
			       myConGuy->getGateway() + " user " +
			       ev.text);
	(toSend->mutex).unlock();
      }
      else if(word1 == "invalid" && word2 == theuser) {
	// Match!  The user the person was looking for is invalid though.
	// Pop off each list and unlock the mutex.
	*Debug << "request EID_INFO match!" << endl;
	requests->waiting.pop_front();
	requests->returnToConGuy.pop_front();
	requests->returnToWho.pop_front();
	*Debug << "popped off waiting list.  request->waiting.size() == "
	       << requests->waiting.size() << endl;
	(requests->mutex).unlock();

	// Let the user that requested know.
	toSend = returnToThisGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( "/w " + returnToThisUser + " " +
			       "INVALID USER @ " + myConGuy->getGateway() +
			       ": " + theuser);
	(toSend->mutex).unlock();
      }
      else if(word1 == "you" && word2 == "are") {
	// "You are ____, .... etc."
	*Debug << "request EID_INFO match!" << endl;
	requests->waiting.pop_front();
	requests->returnToConGuy.pop_front();
	requests->returnToWho.pop_front();
	*Debug << "popped off waiting list.  request->waiting.size() == "
	       << requests->waiting.size() << endl;
	(requests->mutex).unlock();

	// Let the user that requested know.
	// We need to chop up the line first.
	unsigned int pos = ev.text.find( word3.c_str(), 0);
	if(pos == 0 || pos == string::npos) {
	  return false;
	}
	else {
	  toSend = returnToThisGuy->getSendQueue();
	  (toSend->mutex).lock();
	  toSend->out.push_back( "/w " + returnToThisUser + " " +
				 myConGuy->getGateway() + ": " +
				 string(ev.text.substr(pos)));
	  (toSend->mutex).unlock();
	}
      }
      else {
	// Sorry folks, no match this time.
        (requests->mutex).unlock();
      }
    }
    else {
      // The waiting message wasn't a /whereis.  Right now the only EID_INFO
      //   messages we are looking at relate to a /whereis command.
      ///  (/who functionality got scrapped)
      (requests->mutex).unlock();
    }

  } // end case EID_INFO.
    break;
  case EID_ERROR: {
    // Check the error message if it's relevent to our /whereis or /w request.
    //   (If it returned that for the request, the user wasn't logged on.)
    if((ev.text.substr(0, string("That user").size()) == "That user") &&
       (waiting.substr(0, string("/w").size())       == "/w" ||
	waiting.substr(0, string("/whereis").size()) == "/whereis")) {

      // Match!  Pop off each list and unlock the mutex.
      *Debug << "request EID_ERROR match!" << endl;
      requests->waiting.pop_front();
      requests->returnToConGuy.pop_front();
      requests->returnToWho.pop_front();
      *Debug << "popped off waiting list.  request->waiting.size() == "
	     << requests->waiting.size() << endl;
      (requests->mutex).unlock();
      
      // Let the user know.
      toSend = returnToThisGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( "/w " + returnToThisUser + " " +
			       ev.text);
      (toSend->mutex).unlock();
    }
    else {
      // This is some other error message.
      (requests->mutex).unlock();
    }

  } // end case EID_ERROR.
    
    break;
  default:
    // Umm... Only the 3 eventID's above should run this function.
    (requests->mutex).unlock();
    break;
  } // end switch(ev.eventID)
  
  return true;
} // End of bool parse_for_request_status()


//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION:  bool bot::parse_for_commands(event ev, conGuy* myConGuy)
//
// DESCRIPTION: This function parses the event ev for a possible
// command issued by a user.  If the user issued a command, the
// corresponding function is called.
//
// PARAMETERS:
//   event ev
//     This is the event object from which to parse from.
//   conGuy *myConGuy
//     This is the conGuy that the event came from.
//
// RETURN VALUE:
//   bool for an event being successfully parsed.
//
bool bot::parse_for_commands(Event ev, conGuy* myConGuy) {
  string temp;
  unsigned int loc = 0; // Location variable within a string.
  static bool firstRun = true;

  if(firstRun == true) {
    sleep(5); // Give some time for all the conGuy's to get connected
              //   and for a database to be created.
    firstRun = false;
  }

//   // Stop recursive calls to a command.
//   //   Should this be in here?  Is it possible to get in a state where
//   //   the bot never stops servicing requests for itself?  Probably not.
//   if(ev.username == myConGuy->username())
//     return true;
  
  
  // TODO:  Probably should make this trigger configurable.
  // Test if this is a ! triggered command.
  if(   *((ev.text).c_str()) != '!' )
    return true;
  
  // Ok, now we know this started with a !.


  // Let's first extract the command into a string.
  string command;
  temp = (ev.text).substr(1);
  istringstream currLine(temp.c_str());
  currLine >> command;
  // First convert the command to lowercase.
  for(unsigned int i = 0; i < command.size(); i++)
    command[i] = tolower(command[i]);
  *Debug << "DEBUG:  Bot got ! command == " << command << endl;

  // Grab the first argument.
  string arg1;
  currLine >> arg1;
  *Debug << "DEBUG:  Bot got ! arg1 == " << arg1 << endl;

  // Grab the second argument
  string arg2;
  currLine >> arg2;
  *Debug << "DEBUG:  Bot got ! arg2 == " << arg2 << endl;

  // Grab the 3rd arg
  string arg3;
  currLine >> arg3;
  *Debug << "DEBUG:  Bot got ! arg3 == " << arg3 << endl;

  // Grab the 4th arg
  string arg4;
  currLine >> arg4;
  *Debug << "DEBUG:  Bot got ! arg4 == " << arg4 << endl;

  // Grab the 5th arg
  string arg5;
  currLine >> arg5;
  *Debug << "DEBUG:  Bot got ! arg5 == " << arg5 << endl;

  
  // Grab the string after arg1.
  string after_arg1 = "";
  loc = 0;
  loc = (ev.text).find( arg1.c_str(), 0);
  loc += (arg1.size() + 1);

  if(arg1 == "")
    after_arg1 = "";
  else if(loc <= (ev.text).size())
    after_arg1 = (ev.text).substr(loc);
  else
    after_arg1 = "";
  *Debug << "DEBUG:  Bot got ! after_arg1 == " << after_arg1 << endl;
  

  // Grab the string after command.
  string after_command = "";
  loc = 0;
  loc = (ev.text).find( command.c_str(), 0);
  loc += (command.size() + 1);

  if(loc <= (ev.text).size())
    after_command = (ev.text).substr(loc);
  else
    after_command = "";
  *Debug << "DEBUG:  Bot got ! after_command == " << after_command << endl;



  
//////////////////////////////////////////////////////////////////////////////
  
  
  // If myConGuy == bnet, then check if the user is banned, and ignore him.
  if(myConGuy == bnet) {
    if(userIsBanned(ev.username))
      return true;
  }

  // Let's handle non-"bnet conGuy" commands first.
  if(command == "help" || command == "commands") {
    // Convert the arg1 to lowercase if they want help on a command.
    for(unsigned int i = 0; i < arg1.size(); i++)
      arg1[i] = tolower(arg1[i]);
    return cmd_help(ev, myConGuy, command, arg1);
  }

  else if(command == "cointoss")
    return cmd_cointoss(ev, myConGuy);

  else if(command == "dice")
    return cmd_dice(ev, myConGuy, arg1);

  else if(command == "time")
    return cmd_time(ev, myConGuy);

  else if(command == "eastw" || command == "westw" || command == "europew" ||
	  command == "asiaw")
    return cmd_whisper(ev, myConGuy, command, arg1, after_arg1);

  else if(command == "whereis")
    return cmd_whereis(ev, myConGuy, arg1);
  
  else if(command == "uptime")
    return cmd_uptime(ev, myConGuy);
  
  else if(command == "version" || command == "ver")
    return cmd_version(ev, myConGuy);

  // If it got here, we got some weird command that we don't know of.
  //   Just ignore it and pretend we handled it fine.
  if(myConGuy != bnet)
    return true;
  
  // Now we'll look at the bnet-conGuy-only conGuy.  These check for ranks.

  if(userIsGuest(ev.username)) {
    // Guest- commands go here.
    // Commands:  mail, allseen, allseen10, allseen20, banned, operators,
    //   administrators, whois, seen.
    if(command == "mail")
      return cmd_mail(ev, myConGuy, arg1, after_arg1);
    else if(command == "forcemail" || command == "mailforce")
      return cmd_forcemail(ev, myConGuy, arg1, after_arg1);
    else if(command == "inbox")
      return cmd_inbox(ev, myConGuy);
    else if(command == "outbox")
      return cmd_outbox(ev, myConGuy, arg1);
    else if(command == "deloutmail" || command == "rmoutmail") {
      list<string> botmailIDs;
      botmailIDs.push_back(arg1);
      botmailIDs.push_back(arg2);
      botmailIDs.push_back(arg3);
      botmailIDs.push_back(arg4);
      botmailIDs.push_back(arg5);
      return cmd_deloutmail(ev, myConGuy, botmailIDs);
    }
    else if(command == "allseen" || command == "allseen10")
      return cmd_allseen(ev, myConGuy);
    else if(command == "allseen20")
      return cmd_allseen(ev, myConGuy, 20);
    else if(command == "banned")
      return cmd_banned(ev, myConGuy);
    else if(command == "operators")
      return cmd_operators(ev, myConGuy);
    else if(command == "administrators")
      return cmd_administrators(ev, myConGuy);
    else if(command == "whois")
      return cmd_whois(ev, myConGuy, arg1);
    else if(command == "whoami")
      return cmd_whois(ev, myConGuy, ev.username);
    else if(command == "seen")
      return cmd_seen(ev, myConGuy, arg1);
  }

  if(userIsRegular(ev.username)) {
    // Regulars- commands go here.
    // Commands:  smackdown, say, love.
    if(command == "smackdown")
      return cmd_smackdown(ev, myConGuy, arg1);
    else if(command == "love")
      return cmd_love(ev, myConGuy, arg1);
    else if(command == "say")
      return cmd_say(ev, myConGuy, after_command);
  }

  if(userIsOperator(ev.username)) {
    // Operators- commands go here.
    // Commands:  mailops, promote, demote, ban, unban, kick, boot.
    if(command == "mailops")
      return cmd_mailops(ev, myConGuy, after_command);
    else if(command == "promote")
      return cmd_promote(ev, myConGuy, arg1);
    else if(command == "demote")
      return cmd_demote(ev, myConGuy, arg1);
    else if(command == "ban")
      return cmd_ban(ev, myConGuy, arg1, after_arg1);
    else if(command == "unban")
      return cmd_unban(ev, myConGuy, arg1);
    else if(command == "kick" || command == "boot")
      return cmd_kick(ev, myConGuy, arg1, after_arg1);
  }

  if(userIsAdministrator(ev.username)) {
    // Admin commands go here.
    // Commands:  sayexact, promoteusertoadmin, exit.
    if(command == "sayexact")
      return cmd_sayexact(ev, myConGuy, after_command);
    else if(command == "exit")
      return cmd_exit(ev, myConGuy);
    else if(command == "connect")
      return cmd_connect(ev, myConGuy, arg1);
    else if(command == "reconnect")
      return cmd_reconnect(ev, myConGuy, arg1);
    else if(command == "disconnect")
      return cmd_disconnect(ev, myConGuy, arg1);
    else if(command == "promotetoadmin")
      return cmd_promotetoadmin(ev, myConGuy, arg1);
  }
  
  // If it got here, then the person tried to do a command it didn't have
  //   access to or it was some unknown command.
  return true;
} // end of bool parse_for_commands()


//////////////////////////////////////////////////////////////////////////////
// 
// FUNCTION:  bool bot::cmd_help()
//
// DESCRIPTION:  This command will return to the user that requested help, output concerning help for a certain command.  If no command is specified, then available commands for the user is outputed.
//
// RETURN VALUE:
//   bool for successful help returned.
// PARAMETERS:
//   const event ev
//     This is the event to parse from.
//   conGuy *myConGuy
//     This is the conGuy that the event originated from.
//   const string command
//     This is the command of which spawned this function.
//   const string arg1
//     This is either empty, or a name of a command.
//
bool bot::cmd_help(const Event ev, conGuy *myConGuy, const string command, 
		   const string arg1) {

  // Print out the commands available for this conGuy.
  if((command == "help" && arg1 == "") || command == "commands") {
    string outStr;
    sendQueue *toSend = myConGuy->getSendQueue();    
    (toSend->mutex).lock();

    if(myConGuy == bnet) {
      list<string> availCmds;
      // By user rank, find out what commands are available to him/her.
      if(userIsGuest(ev.username)) {
	availCmds.push_back("help");
	availCmds.push_back("commands");
	availCmds.push_back("cointoss");
	availCmds.push_back("dice");
	availCmds.push_back("time");
	availCmds.push_back("uptime");
	availCmds.push_back("version");
	availCmds.push_back("eastw");
	availCmds.push_back("westw");
	availCmds.push_back("europew");
	availCmds.push_back("asiaw");
	availCmds.push_back("whereis");
	availCmds.push_back("mail");
	availCmds.push_back("forcemail");
	availCmds.push_back("inbox");
	availCmds.push_back("outbox");
	availCmds.push_back("deloutmail");
	availCmds.push_back("allseen");
	availCmds.push_back("allseen20");
	availCmds.push_back("banned");
	availCmds.push_back("operators");
	availCmds.push_back("administrators");
	availCmds.push_back("whois");
	availCmds.push_back("seen");
      }
      if(userIsRegular(ev.username)) {
	availCmds.push_back("smackdown");
	availCmds.push_back("love");
	availCmds.push_back("say");
      }
      if(userIsOperator(ev.username)) {
	availCmds.push_back("mailops");
	availCmds.push_back("promote");
	availCmds.push_back("demote");
	availCmds.push_back("ban");
	availCmds.push_back("unban");
	availCmds.push_back("kick");
      }
      if(userIsAdministrator(ev.username)) {
	availCmds.push_back("sayexact");
	availCmds.push_back("exit");
	availCmds.push_back("connect");
	availCmds.push_back("reconnect");
	availCmds.push_back("disconnect");
	availCmds.push_back("promotetoadmin");
      }
      
      
      // Good, we know what commands the person can access now.  Print them 
      //   out.
      outStr = "Available Commands: ";
      int i = 1;
      // The iterations here spit out the commands in chunks of 15 (or less).
      for(list<string>::iterator iter = availCmds.begin(); 
	  iter != availCmds.end(); iter++) {
	if(i % 16 == 0) {
	  // Send and start a new message.
	  toSend->out.push_back(string("/w ") + ev.username + " " + outStr);
	  // Reset outStr.
	  outStr = "Available Commands: ";
	}
	outStr += *iter + string(" ");
	i++;
      }
      // The last bit in outStr won't get sent, so send them out manually.
      // "If the outStr wasn't just the original "Available Commands:  " 
      // (without the commands attached), then we have something important that
      // the user needs to know.  That's when we send.
      if(outStr != "Available Commands: ") {
	// Send the last bit of the commands he/she can use.
	toSend->out.push_back(string("/w ") + ev.username + " " + outStr);
      }

      // Tell him/her how to get more info on a command.
      outStr = string("All commands begin with ") + string("!") + 
	string(" and you can ") + string("get more info on a command by ") +
	string("typing ") + string("!") + string("help <command name>.");
      toSend->out.push_back(string("/w ") + ev.username + " " + outStr);
    }
    else {
      // Whisper back the basic commands only.
      outStr = string("Commands available:  help, commands, ") +
	string("cointoss, dice, ") +
	string("time, uptime, version, eastw, westw, europew, ") +
	string("asiaw, whereis.");
      (toSend->out).push_back(string("/w ") + ev.username + " " + outStr);
      outStr = string("All commands begin with ") + string("!") + 
	string(" and you can ") + string("get more info on a command by ") +
	string("typing ") + string("!") + string("help <command name>.");
      (toSend->out).push_back(string("/w ") + ev.username + " " + outStr);
    }
    (toSend->mutex).unlock();
    return true;
  }

  else if(command == "help") {
    // check out what command they need help with.
    string outStr = "";
    sendQueue *toSend = myConGuy->getSendQueue();    
    (toSend->mutex).lock();

    if(arg1 == "eastw" || arg1 == "westw" || arg1 == "europew" || 
       arg1 == "asiaw") {
      outStr = "This command whispers another person on the specified " +
	string("gateway.  Usage:  !") + arg1 + " <to-user> <message>";
    }
    else if(arg1 == "whereis") {
      outStr = string("This command checks the status of another person") +
	string(" on each of the four gateways.  ") +
	string("Usage:  !whereis <username>");
    }
    else if(arg1 == "cointoss") {
      outStr = string("This command flips a coin and returns the result. ") +
	string("(heads or tails).  Usage:  !cointoss");
    }
    else if(arg1 == "dice") {
      outStr = string("This command rolls a dice.  You can specify the ") +
	string("number of sides the dice has.  Usage:  !dice [num-of-sides]");
    }
    else if(arg1 == "time") {
      outStr = "This command returns the current date and time.  " +
	string("Usage:  !time");
    }
    else if(arg1 == "uptime") {
      outStr = "This command returns how long the bot has been connected to " +
	string("the server.  Usage:  !uptime");
    }
    else if(arg1 == "version") {
      outStr = "This command returns the GiTMbot bot author and the current " +
	string("version running.  Usage:  !version");
    }
    else if(myConGuy != bnet) {
      // Invalid command for help.
      outStr = string("Help:  Unknown command.  Type !commands to see ") +
	string(" available commands.");
    }
    
    if(myConGuy != bnet) {
      // Send out the whisper.
      (toSend->out).push_back(string("/w ") + ev.username + " " + outStr);
      (toSend->mutex).unlock();
      return true;
    }
    else if((myConGuy == bnet) && (outStr != "")) {
      // Send out the whisper.
      (toSend->out).push_back(string("/w ") + ev.username + " " + outStr);
      (toSend->mutex).unlock();
      return true;
    }

    // At this point the person is on the bnet conGuy and is asking for 
    //   help on a command that is not one of the default ones on all
    //   gateways.  ( and outStr == "")
    if(userIsGuest(ev.username)) {
      if(arg1 == "mail")
	outStr = "This command is used to send botmail to a user that is " +
	  string("in our database.  Usage:  !mail <user-to> <message>");
      else if(arg1 == "forcemail")
	outStr = "This command is used to send botmail to a user that is not" +
	  string(" necessarily in our database.  Usage: !mail <user-to> ") +
	  string("<message>");
      else if(arg1 == "inbox")
	outStr = "This command checks for gets any botmail that has been" +
	  string(" sent to you.  Usage:  !inbox");
      else if(arg1 == "outbox")
	outStr = "This command displays a list of info about botmails from " +
	  string("you that have not yet been sent.  ") +
	  string("Usage:  !outbox [user-to]");
      else if(arg1 == "deloutmail")
	outStr = "This command removes up to 5 botmails at a time from " +
	  string("the outbox using the botmail_IDs obtained from the ") +
	  string("!outbox command.  Usage:  !deloutmail <botmail_ID> ") +
	  string("[botmail_ID ...]");
      else if(arg1 == "rmoutmail")
	outStr = "This command removes up to 5 botmails at a time from " +
	  string("the outbox using the botmail_IDs obtained from the ") +
	  string("!outbox command.  Usage:  !rmoutmail <botmail_ID> ") +
	  string("[botmail_ID ...]");
      else if(arg1  == "allseen")
	outStr = "This command displays the last 10 users seen in the " +
	  string("channel.  Usage:  !allseen");
      else if(arg1 == "allseen20")
	outStr = "This command displays the last 20 users seen in the " +
	  string("channel.  Usage:  !allseen20");
      else if(arg1 == "banned" || arg1 == "operators" || 
	      arg1 == "administrators")
	outStr = "This command displays a list of users with " + arg1 +
	  string(" rank/status.  Usage:  !") + arg1;
      else if(arg1 == "whois")
	outStr = "This command displays information about a user.  " +
	  string("Usage:  !whois <username>");
      else if(arg1 == "seen")
	outStr = "This command displays the last time a user was seen in " +
	  string("the channel.  Usage:  !seen <username>");
    }
    if(userIsRegular(ev.username)) {
      if(arg1 == "smackdown")
	outStr = "This command lays a 'smackdown' on a user.  Usage:  " +
	  string("!smackdown <username>");
      else if(arg1 == "love")
	outStr = "This command helps a user express affection to another " +
	  string("user.  Usage:  !love <username>");
      else if(arg1 == "say")
	outStr = "This command has the bot say a message for the user.  " +
	  string("Usage:  !say <message>");
    }
    if(userIsOperator(ev.username)) {
      if(arg1 == "mailops")
	outStr = "This command sends a botmail to all operators.  " +
	  string("Usage:  !mailops <message>");
      else if(arg1 == "promote")
	outStr = "This command promotes a user to a higher rank.  " +
	  string("Usage:  !promote <username>");
      else if(arg1 == "demote")
	outStr = "This command promotes a user to a lower rank.  " +
	  string("Usage:  !demote <username>");
      else if(arg1 == "ban")
	outStr = "This command bans a user from the channel.  " +
	  string("Usage:  !ban <username>");
      else if(arg1 == "unban")
	outStr = "This command unbans a user from the channel.  " +
	  string("Usage:  !unban <username>");
      else if(arg1 == "kick" || arg1 == "boot")
	outStr = "This command kicks a user from the channel.  This " +
	  string("command can be specified with or without an ") +
	  string("accompanying message.  Usage:  !") + arg1 + 
	  string(" <username> [message]");
    }
    if(userIsAdministrator(ev.username)) {
      if(arg1 == "sayexact")
	outStr = "This command forces the bot to say a certain message " +
	  string("exactly as you request.  This is an admin-only command.") +
	  string("  Usage:  !sayexact <message>");
      else if(arg1 == "exit")
	outStr = "This command causes the GiTMbot to exit and shutdown.  " +
	  string("All connections will be terminated.  Usage:  !exit");
      else if(arg1 == "connect")
	outStr = "This command connects an offline bot connection for a " +
	  string("specified gateway.  Usage:  !connect <gateway>");
      else if(arg1 == "reconnect")
	outStr = "This command causes the bot(s) to disconnect and " +
	  string("reconnect to the server for either all gateways or a ") +
	  string("specific gateway.  Usage:  !reconnect [gateway]");
      else if(arg1 == "disconnect")
	outStr = "This command allows you to disconnect one of the bot " +
	  string("connections to a bnet gateway.  You must specify the ") +
	  string("desired gateway.  Usage:  !disconnect <gateway>");
      else if(arg1 == "promotetoadmin")
	outStr = "This command promotes a user to the highest possible" +
	  string(" rank, administrator.  Usage:  !promotetoadmin <username>");
    }
    // If the outStr is still == "", that means they are asking for help
    //   for a command we don't currently support.
    if(outStr == "") {
      outStr = string("Help:  Unknown command.  Type !commands to see ") +
	string(" available commands.");
    }
    // Send out the whisper.
    (toSend->out).push_back(string("/w ") + ev.username + " " + outStr);
    (toSend->mutex).unlock();
    return true;
  }
  return true;
} // End of    bool bot::cmd_help()


//////////////////////////////////////////////////////////////////////////////
// 
// FUNCTION:  bool bot::cmd_cointoss(const event ev, conGuy *myConGuy)
//
// DESCRIPTION: This command will return to the user that requested
// this command, a random cointoss of either heads or tails.
//
// RETURN VALUE:
//   bool for successful output returned.
// PARAMETERS:
//   const event ev
//     This is the event to parse from.
//   conGuy *myConGuy
//     This is the conGuy that the event originated from.
//
bool bot::cmd_cointoss(const Event ev, conGuy *myConGuy) {
  // Cointoss command.
  int toss = rand() % 2;
  string result;
  if(toss == 0)
    result = "Cointoss landed on heads.";
  else if(toss == 1)
    result = "Cointoss landed on tails.";
    
  sendQueue *toSend = myConGuy->getSendQueue();    
  (toSend->mutex).lock();
  if(ev.eventID == EID_WHISPER) {
    // Whisper back the result.
    (toSend->out).push_back(string("/w ") + ev.username + " " + result);
  }
  else {
    // Send it back in chat.
    (toSend->out).push_back(result);
  }
  (toSend->mutex).unlock();
  return true;
} // End of    bool bot::cmd_cointoss()


//////////////////////////////////////////////////////////////////////////////
// 
// FUNCTION:  bool bot::cmd_dice(const event ev, conGuy *myConGuy, 
//                               const string sides)
//
// DESCRIPTION: This command will return the result of rolling an
// n-sided dice.  The default is 6 sides.
//
// RETURN VALUE:
//   bool for successful output returned.
// PARAMETERS:
//   const event ev
//     This is the event to parse from.
//   conGuy *myConGuy
//     This is the conGuy that the event originated from.
//   const string sides
//     This is the number of sides on the dice, if specified.
//
bool bot::cmd_dice(const Event ev, conGuy *myConGuy, const string sides) {

  // This is a working char string used to convert numbers to std::strings.
  char temp[16];

  // Find number of sides as an int.
  int n_sides = atoi(sides.c_str());

  // Make sure that it's a valid dice number.  Default to 6.
  if((sides == "") || (n_sides == 0)) {
    n_sides = 6;
  }

  // Get n_sides as a string.
  sprintf(temp, "%d", n_sides);
  string n_sidesStr = string(temp);

  // We do this as a 3-way cointoss.
  int toss = (rand() % n_sides) + 1;
  // Get toss result as a string.
  sprintf(temp, "%d", toss);
  string tossStr = string(temp);

  string result = string("The ") + n_sidesStr + 
    string("-sided dice rolled a ") + tossStr + string(".");
    
  sendQueue *toSend = myConGuy->getSendQueue();    
  (toSend->mutex).lock();
  if(ev.eventID == EID_WHISPER) {
    // Whisper back the result.
    (toSend->out).push_back(string("/w ") + ev.username + " " + result);
  }
  else {
    // Send it back in chat.
    (toSend->out).push_back(result);
  }
  (toSend->mutex).unlock();
  return true;
} // End of    bool bot::cmd_dice()


//////////////////////////////////////////////////////////////////////////////
// 
// FUNCTION:  bool bot::cmd_time(const Event ev, conGuy *myConGuy)
//
// DESCRIPTION: This command will return to the user that requested
// this command, the current local time at the computer running this bot.
//
// RETURN VALUE:
//   bool for successful output returned.
// PARAMETERS:
//   const Event ev
//     This is the Event to parse from.
//   conGuy *myConGuy
//     This is the conGuy that the Event originated from.
//
bool bot::cmd_time(const Event ev, conGuy *myConGuy) {
  time_t rawtime;
  struct tm * timeinfo;

  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  // I have to do this hackish thing because apparently the asctime function
  //   returns a weird non-printable character at the end 0x0a.  My only
  //   get-around is to extract what I want from it piece by piece.
  string day, month, date, time, year;
  string temp = string(asctime(timeinfo));  // Our start string.
  istringstream currLine(temp.c_str());
  currLine >> day;
  currLine >> month;
  currLine >> date;
  currLine >> time;
  currLine >> year;

  string currTime = day + " " + month + " " + date + " " +
    time + " " + year;
  string tosay = string("It is ") + currTime + string(".");
  sendQueue *toSend = myConGuy->getSendQueue();    
  (toSend->mutex).lock();
  if(ev.eventID == EID_WHISPER) {
    // Whisper back the time.
    (toSend->out).push_back(string("/w ") + ev.username + " " + tosay);
  }
  else {
    // Send it back in chat.
    (toSend->out).push_back(tosay);
  }
  (toSend->mutex).unlock();
  return true;
} // End of     bool bot::cmd_time(const Event ev, conGuy *myConGuy) 



// TODO:  finish commenting this file from here down.

bool bot::cmd_whisper(const Event ev, conGuy *myConGuy, 
		      const string command, const string arg1, 
		      const string after_arg1) {
  conGuy* toExeOn = 0;

  if(command == "eastw") {
    toExeOn = useast;
  }
  else if(command == "westw") {
    toExeOn = uswest;
  }
  else if(command == "europew") {
    toExeOn = europe;
  }
  else if(command == "asiaw") {
    toExeOn = asia;
  }
  else {
    // toExeOn stays 0 and it'll fall out of all if's coming up and return.
  }


  // Check if the conGuy we want to whisper to is connected.
  if(toExeOn != 0 && toExeOn->isConnected() == false) {
    // Let the user know we can't whisper to a conGuy that's not connected.
    sendQueue *toSend = myConGuy->getSendQueue();
    string tosay = toExeOn->getGateway() + string(" server is not connected.");
    (toSend->mutex).lock();
    (toSend->out).push_back(string("/w ") + ev.username + " " + tosay);
    (toSend->mutex).unlock();
    return true;
  }

  // If the command and user-to-whisper-to was specified, but no msg.
  if(after_arg1 == "") {
    // Don't send since it'll mess up our requests queue.
    sendQueue *toSend = myConGuy->getSendQueue();
    string tosay = "Whisper was blank, so it was not sent.";
    (toSend->mutex).lock();
    (toSend->out).push_back(string("/w ") + ev.username + " " + tosay);
    (toSend->mutex).unlock();
  }
  else if(toExeOn != 0) {
    string tosay;
    tosay = ev.username;
    tosay = tosay + " @ " + myConGuy->getGateway();
    tosay = tosay + " says: " + after_arg1;
    // Request send on the conGuy.
    sendRequests *requests = toExeOn->getSendRequests();
    (requests->mutex).lock();
    (requests->out).push_back(string("/w ") + arg1 + " " + tosay);
    (requests->fromConGuy).push_back(myConGuy);
    (requests->fromWho).push_back(ev.username);
    (requests->mutex).unlock();
  }
  return true;
}


bool bot::cmd_whereis(const Event ev, conGuy *myConGuy, 
		      const string arg1) {
  // Request send on the conGuy.
  *Debug << "got to the whereis command parsing for request processing"
	 << endl;

  if(arg1 == "")
    return cmd_help(ev, myConGuy, "help", "whereis");

  // Do it for useast.
  if(useast->isConnected()) {
    sendRequests *requests = useast->getSendRequests();
    (requests->mutex).lock();
    (requests->out).push_back(string("/whereis ") + arg1);
    (requests->fromConGuy).push_back(myConGuy);
    (requests->fromWho).push_back(ev.username);
    (requests->mutex).unlock();
    *Debug << "queued a request for useast" << endl;
  }

  // Do it for uswest.
  if(uswest->isConnected()) {
    sendRequests *requests = uswest->getSendRequests();
    (requests->mutex).lock();
    (requests->out).push_back(string("/whereis ") + arg1);
    (requests->fromConGuy).push_back(myConGuy);
    (requests->fromWho).push_back(ev.username);
    (requests->mutex).unlock();
    *Debug << "queued a request for uswest" << endl;
  }

  // Do it for europe.
  if(europe->isConnected()) {
    sendRequests *requests = europe->getSendRequests();
    (requests->mutex).lock();
    (requests->out).push_back(string("/whereis ") + arg1);
    (requests->fromConGuy).push_back(myConGuy);
    (requests->fromWho).push_back(ev.username);
    (requests->mutex).unlock();
    *Debug << "queued a request for europe" << endl;
  }

  // Do it for asia.
  if(asia->isConnected()) {
    sendRequests *requests = asia->getSendRequests();
    (requests->mutex).lock();
    (requests->out).push_back(string("/whereis ") + arg1);
    (requests->fromConGuy).push_back(myConGuy);
    (requests->fromWho).push_back(ev.username);
    (requests->mutex).unlock();
    *Debug << "queued a request for asia" << endl;
  }

  return true;
}

bool bot::cmd_version(const Event ev, conGuy *myConGuy) {
  sendQueue *toSend;
  char versionStr[32];
  sprintf(versionStr, "%.2f", VERSION);

  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( string("/w ") + ev.username +
			 string(" GiTMbot version ") + string(versionStr) +
			 string(" brought to you by GiTM.soccerist."));
  (toSend->mutex).unlock();
  return true;
}

bool bot::cmd_say(const Event ev, conGuy *myConGuy, const string msg) {
  sendQueue *toSend;

  if(msg == "")
    return cmd_help(ev, myConGuy, "help", "say");

  // Say it!
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( ev.username +
			 string(" says ") + msg);
  (toSend->mutex).unlock();
  return true;
}

bool bot::cmd_sayexact(const Event ev, conGuy *myConGuy, const string msg) {
  sendQueue *toSend;

  if(msg == "")
    return cmd_help(ev, myConGuy, "help", "sayexact");
  
  // Say it!
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( msg);
  (toSend->mutex).unlock();

  return true;
}


// This function takes care of the !mail command.  It will not queue mail
// if the to-user doesn't exist in the database.
bool bot::cmd_mail(const Event ev, conGuy *myConGuy, const string mailTo, 
		   const string msg) {
  string fromUser = ev.username;
  string toUser   = mailTo;
  sendQueue *toSend;

  // If the user we are mailing doesn't exist, inform the person and request
  //   that the person use !forcemail instead. 
  if( ! user_exists(toUser)) {
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" This user isn't in our database.") +
			   string("  If you really want to mail \"") +
			   toUser + string("\", please use the !forcemail ") +
			   string("command."));
    (toSend->mutex).unlock();
  }
  else if(queue_mail(fromUser, toUser, msg)) {
    // Tell fromUser that mail was queued for delivery.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" Mail queued for delivery."));
    (toSend->mutex).unlock();
  }
  else {
    // Tell him that mailing failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" Attempt to mail failed."));
    (toSend->mutex).unlock();
  }
  return true;
}

// This function takes care of the !forcemail command.  This is here for the
// special case where the user wants to mail a person that doesn't currently
// exist in our database.
bool bot::cmd_forcemail(const Event ev, conGuy *myConGuy, const string mailTo, 
		   const string msg) {
  string fromUser = ev.username;
  string toUser   = mailTo;
  sendQueue *toSend;

  // Here we are mailing the user regardless of if either user exists in our
  //   database.  The to and from users will be new users if they don't exist
  //   already.
  if(queue_mail(fromUser, toUser, msg)) {
    // Tell fromUser that mail was queued for delivery.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" Mail queued for delivery."));
    (toSend->mutex).unlock();
  }
  else {
    // Tell him that mailing failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" Attempt to mail failed."));
    (toSend->mutex).unlock();
  }
  return true;
}

bool bot::cmd_mailops(const Event ev, conGuy *myConGuy, const string msg) {
  sendQueue *toSend;

  if(mail_ops(ev.username, msg)) {
    // Tell fromUser that mail was queued for delivery.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" Mail to all operators+ queued for ") +
			   string("delivery."));
    (toSend->mutex).unlock();
  }
  else {
    // Tell him that mailing failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" Attempt to mail operators failed."));
    (toSend->mutex).unlock();
  }
  return true;  
}

bool bot::cmd_allseen(const Event ev, conGuy *myConGuy, const int number) {
  sendQueue *toSend;
  list<string> lastSeenUsers;

  if(allseen(lastSeenUsers, number)) {
    string tosay;
    int i = 1;
    tosay = "Users last seen: ";

    for(list<string>::iterator iter = lastSeenUsers.begin(); 
	iter != lastSeenUsers.end(); iter++) {
      if(i % 11 == 0) {
	// Send and start a new message.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
	(toSend->mutex).unlock();

	// Reset tosay.
	tosay = "Users last seen: ";
      }
      tosay += *iter + string(" ");
      i++;
    }
    if(tosay != "Users last seen: ") {
      // Send the last bit of the users.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
      (toSend->mutex).unlock();
    }
  }
  else {
    // Allseen failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + " " +
			   string("Allseen request failed."));
    (toSend->mutex).unlock();
  }
  return true;
}


bool bot::cmd_allseen(const Event ev, conGuy *myConGuy) {
  return cmd_allseen(ev, myConGuy, 10);
}

bool bot::cmd_inbox(const Event ev, conGuy *myConGuy) {
  string userfrom;
  string message;
  time_t queueTime;      // Time that the userfrom sent the mail.
  struct tm * timeinfo;  // Same as above except in tm form.
  sendQueue *toSend;

  if(! has_mail(ev.username)) {
    // Tell the user he has no mail.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + " " +
			   string("You have no mail."));
    (toSend->mutex).unlock();
    return true;
  }

  // While there is a message that we'll get and delete from the database.
  while(check_mail(ev.username, userfrom, queueTime, message) &&
	remove_mail(userfrom, ev.username, queueTime)) {
    timeinfo = localtime ( &queueTime );
    // I have to do this hackish thing because apparently the asctime function
    //   returns a weird non-printable character at the end 0x0a.  My only
    //   get-around is to extract what I want from it piece by piece.
    string day, month, date, time, year;
    string temp = string(asctime(timeinfo));  // Our start string.
    istringstream currLine(temp.c_str());
    currLine >> day;
    currLine >> month;
    currLine >> date;
    currLine >> time;
    currLine >> year;

    string mailTime = day + " " + month + " " + date + " " +
      time + " " + year;

    // Give the user his most recent mail.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + " " + 
			   string("Botmail from ") + userfrom + string(" at ")+
			   mailTime + ": " + message);
    (toSend->mutex).unlock();
  }
  return true;
}


// This function gives the user all outgoing botmails from him/her
//  (optionally to a certain user).  In this function,
//   I am using the botmail_ID as the same thing as queueTime in absolute 
//   time_t time.
//
// I think the best way to do this is to keep a running list of 'fittable'
// botmails retrieved.  Maybe a new datatype for this.  (*groan*).  That way
// when I find the fitting case, It can be extracted by timestamp/mailTime
// at the very end when I'm ready.
// The list would start by matching properties left to right, starting with
// userFrom (ev.username), then userTo.  Each time we go to the
// next variable, we parse through our list and remove non-fitting qualities.
// At the end we can sort/parse the mail's timestamp for the order to return
// the results.
// 
// Yes.  A type was created.  It is this, which is defined in bot.h.
// class botmail {
// public:
//   string userTo;
//   string userFrom;
//   string msg;
//   time_t queueTime; 
// };
// 
bool bot::cmd_outbox(const Event ev, conGuy *myConGuy, string userTo) {

  list <botmail> matchingMails;  // Our running list of 'fittable' mails.
  botmail currBotmail;  // Current botmail pulled from the database.
  sendQueue *toSend = 0;
  int i = 0;
  int rc;
  char *err = 0;
  string userFrom = ev.username;

  
  // First thing to do is convert the names to lowercase.
  for(i = 0; i < static_cast<int>(userFrom.size()); i++)
  {
    userFrom[i] = tolower(userFrom[i]);
  }
  for(i = 0; i < static_cast<int>(userTo.size()); i++)
  {
    userTo[i] = tolower(userTo[i]);
  }
  
  
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;


  // Make the exeStr
  exeStr = sqlite_mprintf("select * from botmails where fromuser = %Q;",
			  userFrom.c_str());
  
  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    if(exeStr != 0) {
      sqlite_freemem(exeStr);
      exeStr = 0;
    }
    return false;
  }

  rc = SQLITE_ROW;  // Just to get started in the while loop.
  while(rc == SQLITE_ROW) {
    // Step through the results by row.
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    // If there's an error, report error and clean up and return false.
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      // Finalize the virtual machine.  (cleanup)
      rc = sqlite_finalize(virtualMachine, &err);
      if(rc != SQLITE_OK && err != 0) {
	botlog << "SQL ERROR:  " << string(err) << endl;
	free(err);
	err = 0;
      }
      if(exeStr != 0) {
	sqlite_freemem(exeStr);
	exeStr = 0;
      }
      if(err != 0) {
	free(err);
	err = 0;
      }
      return false;
    }
    else if(rc == SQLITE_DONE) {
      // We're finished.  We have all the mail times for this user.
      //   This is our last time through the while.
    }
    else if(rc == SQLITE_ROW) {
      // Good, precisely what we're looking for.
      // Grab all the data into a botmail type and push it back onto the list.

      // Here is the create command I used to create the table 'botmails'. 
      // And so yeah, they are the datatypes in the columnData return values.

      // From the open_sql_db() function somewhere else in this file:
      // rc = sqlite_exec( db, "create table botmails (touser varchar(32), 
      //                   fromuser varchar(32), time number(10), 
      //                   message varchar(255));", 0, 0, &err);

      // Store data.
      currBotmail.userTo    = string(columnData[0]);
      currBotmail.userFrom  = string(columnData[1]);
      currBotmail.queueTime = (time_t)atoi(columnData[2]);
      currBotmail.msg       = string(columnData[3]);
      // Put into list.  
      matchingMails.push_back(currBotmail);
    }
  }  // end of     while(rc == SQLITE_ROW)

  // We're done with dealing with the database since we have all that we want.
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  // At this point, we have our first list of all the botmails the user
  // has queued in his outbox.  
  
  // Check to see if there are no outgoing mails by the user.  
  if(matchingMails.size() == 0) {
    // Well, no matches, we'll just pass the stuff to our helper function.
    returnResultsToCmdOutbox(myConGuy, userFrom, matchingMails);
    return true;
  }
  
  // Check for an empty userTo.
  if(userTo == "") {
    // Return all of the calling user's botmails.
    returnResultsToCmdOutbox(myConGuy, userFrom, matchingMails);
    return true;
  }

  bool found = false;  // To detect if userTo is in the matchingMails list.
  for(list<botmail>::const_iterator iter = matchingMails.begin();
      iter != matchingMails.end(); iter++) {
    if( (*iter).userTo == userTo)
      found = true;
  }
  
  if(found == true) {
    // Need to update the matchingMails list to only hold mails from userTo.
    list<botmail> newMatches;
    for(list<botmail>::const_iterator iter = matchingMails.begin();
	iter != matchingMails.end(); iter++) {
      if( (*iter).userTo == userTo) {
	// Match. Put this on the newMatches list.
	newMatches.push_back(*iter);
      }
    } 
    // Copy over the list of new matches to the matchingMails list.
    matchingMails = newMatches;
    // matchingMails is now as updated as we can make it.
    returnResultsToCmdOutbox(myConGuy, userFrom, matchingMails);
    return true;
  }
  else if(found == false) {
    // The userTo wasn't found in our list of outgoing mails.
    // Let the user know.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    (toSend->out).push_back(string("/w ") + userFrom + " " + 
			    string("There were no out-going botmails found ") +
			    string("to user ") + userTo);
    (toSend->mutex).unlock();
    return true;
  }
  return true;
}


bool bot::returnResultsToCmdOutbox(conGuy *myConGuy, string userFrom, 
				   list<botmail> matchingMails) {
  sendQueue *toSend = 0;

  if(matchingMails.size() == 0) {
    // Let the user know he has no mail in his outbox.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + userFrom + " " +
			   string("Your outbox is empty."));
    (toSend->mutex).unlock();
    return true;
  }

  // While there are still mails to report, report them!
  while(matchingMails.size() > 0) {
    botmail oldestBotmail;
    time_t oldestBotmailTime = LONG_MAX;  // set/reset the oldestBotmailTime.
    
    list<botmail>::iterator iter;

    // Find the time to the oldestBotmail.
    for(iter = matchingMails.begin(); iter != matchingMails.end(); iter++) {
      if( (*iter).queueTime < oldestBotmailTime) {
	// Found a new oldest botmail.
	oldestBotmailTime = (*iter).queueTime;
      }
    }
    // Use the oldestBotmailTime to find the oldestBotmail.
    for(iter = matchingMails.begin(); iter != matchingMails.end(); iter++) {
      if( (*iter).queueTime == oldestBotmailTime) {
	// We found it.
	oldestBotmail = (*iter);
	// Remove this botmail from the list.
	matchingMails.erase(iter);
	break; // Break out of the for loop.  We found the oldest mail already.
      }
    }

    // Give the user his oldest mail.

    // Arg at my suckyness of time_t to string conversion.
    char iSuckAtConversions[32];
    sprintf(iSuckAtConversions, "%li", oldestBotmail.queueTime);
    string botmail_ID = string(iSuckAtConversions);

    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + userFrom + " " + 
			   string("[botmail_ID: ") + botmail_ID + 
			   string("] [To: ")       + oldestBotmail.userTo +
			   string("] [Msg: ")      + oldestBotmail.msg +
			   string("]"));
    (toSend->mutex).unlock();
  } // end of    while(matchingMails.size() > 0)

  return true;
}  // end of    bool bot::returnResultsToCmdOutbox()



// This function removes up to 5 outgoing botmails using the absolute time_t 
//   time mailTime as reference or as the parameter calls it, botmail_IDs.  
//   It's in string format so we'll have to be
//   sure to change it to unsigned long int later.
bool bot::cmd_deloutmail(const Event ev, conGuy *myConGuy,
			 list<string> botmailIDs) {
  sendQueue *toSend = 0;
  string userFrom = ev.username;

  // First convert userTo and userFrom to lowercase.
  for(unsigned int i = 0; i < userFrom.size(); i++)
    userFrom[i] = tolower(userFrom[i]);

  // Count the number of mailTimes (botmailIDs) specified
  // that are non-blank.  Then remove all the badly formatted botmailIDs.
  int totalBotmailIDs = 0;
  int totalRemovedBotmailIDs = 0;



  while(botmailIDs.size() != 0) {
    string currBotmailID = botmailIDs.front();

    if( currBotmailID != "") {
      // If it's not blank, count it towards our totalBotmailIDs count.
      totalBotmailIDs++;
    }
    else {
      // The current botmailID is blank, so remove it from the list.
      botmailIDs.pop_front();
      continue;  // Restart the WHILE loop.
    }

    // If it's badly formatted, remove it from the list.
    // Check to make sure mailTime actually only contains numbers.
    for(string::const_iterator j = currBotmailID.begin(); 
	j != currBotmailID.end(); j++) {
      if(! isdigit(*j)) {
	// Yikes, we have non-numbers!  Tsk tsk.  This botmail gets removed
	//   from the list.
	botmailIDs.pop_front();
	continue;                // Restart the WHILE loop.
      }
    }


    // Try to remove the mail.  This will inherently verify that there is 
    // a botmail existant for the time it's for and let us
    // know by it's return value. 
    // Convert to a time_t number.
    time_t queueTime = atoi( currBotmailID.c_str());

    // If the remove succeeded
    if(remove_mail(userFrom, queueTime)) {
      // Nice... it worked.  :)  Increment our counter variable that it worked.
      totalRemovedBotmailIDs++;

      botmailIDs.pop_front();     // Remove the element from the list.
      continue;                // Restart the WHILE loop.
    }
    // If the remove failed
    else {
      // It didn't find the botmail or sqlite fucked up somewhere.  Most likely
      //   user error.  *_*  Just continue on.

      botmailIDs.pop_front();     // Remove the element from the list.
      continue;                // Restart the WHILE loop.
    }
  } // end of   while(botmailIDs.size() != 0)


  // Give the user the results of the delete/remove of the botmails.

  // Convert the int to a c_str from so we can turn it into a string.
  char totalBotmailIDs_c_str[8];
  char totalRemovedBotmailIDs_c_str[8];
  sprintf(totalBotmailIDs_c_str, "%d", totalBotmailIDs);
  sprintf(totalRemovedBotmailIDs_c_str, "%d", totalRemovedBotmailIDs);

  // Give the user the results.
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( string("/w ") + ev.username + " " +
			 string(totalRemovedBotmailIDs_c_str) + " out of " +
			 string(totalBotmailIDs_c_str) +
			 string(" botmails were removed as requested."));
  (toSend->mutex).unlock();

  return true;
} // end of   bool cmd_deloutmail()


bool bot::cmd_banned(const Event ev, conGuy *myConGuy) {
  sendQueue *toSend;
  list<string> bannedUsers;

  if(banned(bannedUsers)) {
    if(bannedUsers.size() == 0) {
      // There are no banned users.  Let him/her know.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + " " + 
			     string("There are no banned users."));
      (toSend->mutex).unlock();
      return true;
    }
    
    // Ok, let the user know what the users are that are banned.
    string tosay;
    int i = 1;
    tosay = "Banned users: ";

    for(list<string>::iterator iter = bannedUsers.begin(); 
	iter != bannedUsers.end(); iter++) {
      if(i % 11 == 0) {
	// Send and start a new message.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
	(toSend->mutex).unlock();

	// Reset tosay.
	tosay = "Banned users: ";
      }
      tosay += *iter + string(" ");
      i++;
    }
    if(tosay != "Banned users: ") {
      // Send the last bit of the users.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
      (toSend->mutex).unlock();
    }
  }
  else {
    // !banned failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + " " +
			   string("List of banned users request failed."));
    (toSend->mutex).unlock();
  }
  return true;
}


bool bot::cmd_operators(const Event ev, conGuy *myConGuy) {
  sendQueue *toSend;
  list<string> operatorUsers;

  if(operators(operatorUsers)) {
    if(operatorUsers.size() == 0) {
      // There are no operator users.  Let him/her know.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + " " + 
			     string("There are no operators."));
      (toSend->mutex).unlock();
      return true;
    }
    
    // Ok, let the user know what the users are that are operators.
    string tosay;
    int i = 1;
    tosay = "Operators: ";

    for(list<string>::iterator iter = operatorUsers.begin(); 
	iter != operatorUsers.end(); iter++) {
      if(i % 11 == 0) {
	// Send and start a new message.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
	(toSend->mutex).unlock();

	// Reset tosay.
	tosay = "Operators: ";
      }
      tosay += *iter + string(" ");
      i++;
    }
    if(tosay != "Operators: ") {
      // Send the last bit of the users.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
      (toSend->mutex).unlock();
    }
  }
  else {
    // !operators failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + " " +
			   string("List of rank operator users request ") +
			   string("failed."));
    (toSend->mutex).unlock();
  }
  return true;
}


bool bot::cmd_administrators(const Event ev, conGuy *myConGuy) {
  sendQueue *toSend;
  list<string> administratorUsers;

  if(administrators(administratorUsers)) {
    if(administratorUsers.size() == 0) {
      // There are no administrator users.  Let him/her know.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + " " + 
			     string("There are no administrators."));
      (toSend->mutex).unlock();
      return true;
    }
    
    // Ok, let the user know what the users are that are administrators.
    string tosay;
    int i = 1;
    tosay = "Administrators: ";

    for(list<string>::iterator iter = administratorUsers.begin(); 
	iter != administratorUsers.end(); iter++) {
      if(i % 11 == 0) {
	// Send and start a new message.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
	(toSend->mutex).unlock();

	// Reset tosay.
	tosay = "Administrators: ";
      }
      tosay += *iter + string(" ");
      i++;
    }
    if(tosay != "Administrators: ") {
      // Send the last bit of the users.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + " " + tosay);
      (toSend->mutex).unlock();
    }
  }
  else {
    // !administrators failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + " " +
			   string("List of rank administrator users request ")+
			   string("failed."));
    (toSend->mutex).unlock();
  }
  return true;
}


bool bot::cmd_whois(const Event ev, conGuy *myConGuy, const string name) {
  sendQueue *toSend;
  string rank;
  time_t lastSeen;
  struct tm * lastSeenTimeInfo;

  if(! user_exists(name)) {
    // Tell him/her that the user either doesn't exist or is unknown.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + " " +
			   string("User ") + name + 
			   string(" unknown.  He/she isn't in our database."));
    (toSend->mutex).unlock();
    return true;
  }

  // What rank is this user?
  if(userIsGuest(name))
    rank = "GUEST";
  if(userIsRegular(name))
    rank = "REGULAR";
  if(userIsOperator(name))
    rank = "OPERATOR";
  if(userIsAdministrator(name))
    rank = "ADMINISTRATOR";
  
  // Get his/her last seen time.
  lastSeen = seenUser(name);
  lastSeenTimeInfo = localtime( &lastSeen);
  

  // Prepare it into a string.  

  // Ok, this is what I have found to be very annoying and is baffling me.
  //   Doing the string(asctime(lastSeenTimeInfo)) seems to append a 0x0a
  //   character at the end.  It makes bnet mad, so bnet won't send that
  //   message.  ARG.  So what I'm doing is hacking around it and just
  //   compiling it by breaking the time string into tiny bits and summing
  //   their strings.


  string day, month, date, time, year;
  string temp = string(asctime(lastSeenTimeInfo));  // Our start string.
  istringstream currLine(temp.c_str());
  currLine >> day;
  currLine >> month;
  currLine >> date;
  currLine >> time;
  currLine >> year;

  // Tell him/her that the user either doesn't exist or is unknown.
  string tosay = string("/w ") + ev.username + string(" ") +
    string("User ") + name;
  if(userIsBanned(name))
    tosay += string(" is banned, ");
  tosay += string(" has rank ") + rank + string(" and was last seen ") + 
    day + " " + month + " " + date + " " + time + " " + year + ".";
  
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( tosay);
  (toSend->mutex).unlock();
  return true;
}


bool bot::cmd_promote(const Event ev, conGuy *myConGuy, const string name) {
  sendQueue *toSend;

  // The calling user is the ev.username after turned into lowercase.
  // This needs to be in lowercase because we call database functions on it.
  // (And we store in lowercase in our database.)
  string callingUser = ev.username;
  for(unsigned int i = 0; i < callingUser.size(); i++)
    callingUser[i] = tolower(callingUser[i]);


  // Right now, operators can only promote.
  if(userIsOperator(callingUser)) {
    if(! userIsAdministrator(callingUser)) {
      // This means that the callingUser is exactly rank Operator.
      if(userIsAdministrator(name)) {
	// Let him know that admin is already the highest rank.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " +
			       string("You're only an operator and can't ") +
			       string("promote an administrator to a rank ") +
			       string("higher than administrator."));
	(toSend->mutex).unlock();
	return true;
      }
      else if(userIsOperator(name)) {
	// Ops can't create admins.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " +
			       string("You're only an operator and aren't ") +
			       string("allowed to promote other operators ") +
			       string("to administrator rank."));
	(toSend->mutex).unlock();
	return true;
      }
      else if(userIsRegular(name)) {
	// Currently, operators can't promote other regulars to operator status.
	// Let the user know.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " +
			       string("You're only an operator and aren't ") +
			       string("allowed to create other operators."));
	(toSend->mutex).unlock();
	return true;
      }
      else if(userIsGuest(name)) {
	// Last possible case.  Promote is allowed.
	if(promoteUser(name)) {
	  // What is the user's new rank?
	  string rank;
	  if(userIsGuest(name))
	    rank = "GUEST";
	  if(userIsRegular(name))
	    rank = "REGULAR";
	  if(userIsOperator(name))
	    rank = "OPERATOR";
	  if(userIsAdministrator(name))
	    rank = "ADMINISTRATOR";

	  // Let the person know he promoted successfully.
	  toSend = myConGuy->getSendQueue();
	  (toSend->mutex).lock();
	  toSend->out.push_back( string("/w ") + ev.username +
				 string(" User ") + name + 
				 string(" promoted to rank ") + rank + ".");
	  (toSend->mutex).unlock();
	}
	else {
	  // Let the person know the promote failed.
	  toSend = myConGuy->getSendQueue();
	  (toSend->mutex).lock();
	  toSend->out.push_back( string("/w ") + ev.username +
				 string(" User ") + name + 
				 string(" not promoted."));
	  (toSend->mutex).unlock();
	}
	return true;
      } // end of   else if(userIsGuest(name))
    } // end of    if(! userIsAdministrator(callingUser))
    else {
      // The callingUser is a real admin.
      if(userIsAdministrator(name)) {
	// Let him know that admin is already the highest rank.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username + " " +
			       string("User ") + name + 
			       string(" is the highest rank already."));
	(toSend->mutex).unlock();
	return true;
      }
      // A do-able promote.
      else if(promoteUser(name)) {
	// What is the user's new rank?
	string rank;
	if(userIsGuest(name))
	  rank = "GUEST";
	if(userIsRegular(name))
	  rank = "REGULAR";
	if(userIsOperator(name))
	  rank = "OPERATOR";
	if(userIsAdministrator(name))
	  rank = "ADMINISTRATOR";

	// Let the person know he promoted successfully.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username +
			       string(" User ") + name + 
			       string(" promoted to rank ") + rank + ".");
	(toSend->mutex).unlock();
      }
      else {
	// Let the person know the promote failed.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username +
			       string(" User ") + name + 
			       string(" not promoted."));
	(toSend->mutex).unlock();
      }
      return true;
    } // End of case where the callingUser is a real admin.
  }
  else {
    // Let the person know he's not allowed to promote.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" You must be at least an operator to ") +
			   string("promote."));
    (toSend->mutex).unlock();
  }
  return true;
}


bool bot::cmd_promotetoadmin(const Event ev, conGuy *myConGuy, 
			     const string name) {
  sendQueue *toSend;

  if(!userIsAdministrator(ev.username)) {
    // Let the user know he must be an admin to promote someone to rank admin.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" You must be at least an admin to ") +
			   string("promote people to admin."));
    (toSend->mutex).unlock();
    return true;
  }
  
  if(promoteUserToAdmin(name)) {
    // Let the person know he promoted successfully.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" User ") + name + 
			   string(" promoted to rank ADMINISTRATOR."));
    (toSend->mutex).unlock();
  }
  else {
    // Let the user know promotion failed.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" User ") + name + 
			   string(" not promoted."));
    (toSend->mutex).unlock();
  }
  return true;
}

bool bot::cmd_demote(const Event ev, conGuy *myConGuy, const string name) {
  sendQueue *toSend;

  // Right now, operators can only demote.
  if(userIsOperator(ev.username)) {

    // Non-admins can't demote other operators or admins.
    if((userIsAdministrator(name)) &&  (! userIsAdministrator(ev.username))) {
      // He's an operator trying to demote an admin.
      //   tsk tsk.  
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username +
			     string(" You are an operator so you can't ") +
			     string("demote admins.  Admins are gods!!"));
      (toSend->mutex).unlock();
    }
    else if((userIsOperator(name)) &&  (! userIsAdministrator(ev.username))) {
      // He's an operator and is trying to demote another operator.
      // Don't let him.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username +
			     string(" You are an operator and can't ") +
			     string("demote other operators.  Sorry!"));
      (toSend->mutex).unlock();
    }
    else {
      // A legit demote.
      if(demoteUser(name)) {
	string rank;
	if(userIsGuest(name))
	  rank = "GUEST";
	if(userIsRegular(name))
	  rank = "REGULAR";
	if(userIsOperator(name))
	  rank = "OPERATOR";
	if(userIsAdministrator(name))
	  rank = "ADMINISTRATOR";

	// Let the person know he demoted successfully.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username +
			       string(" User ") + name + 
			       string(" demoted to rank ") + rank + ".");
	(toSend->mutex).unlock();
      }
      else {
	// Let the person know the demote failed.
	toSend = myConGuy->getSendQueue();
	(toSend->mutex).lock();
	toSend->out.push_back( string("/w ") + ev.username +
			       string(" User ") + name + 
			       string(" not demoted."));
	(toSend->mutex).unlock();
      }
    }
  }
  else {
    // Let the person know he's not allowed to demote.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" You must be at least an operator to ") +
			   string("demote."));
    (toSend->mutex).unlock();
  }
  return true;
}

bool bot::cmd_seen(const Event ev, conGuy *myConGuy, const string name) {
  sendQueue *toSend;
  char tempStr[128];
  time_t currTime;
  time_t seenTime;
  struct tm * timeinfo;
  
  long secondsDifference;
  int daysAgo;
  int hoursAgo;
  int minutesAgo;
  string daysAgoStr;
  string hoursAgoStr;
  string minutesAgoStr;

  const int secondsInDay  = 60 * 60 * 24;
  const int secondsInHour = 60 * 60;
  const int secondsInMinute = 60;

  seenTime = seenUser(name);
  timeinfo = localtime( &seenTime);
  time( &currTime);
  
  if(seenTime == 0) {
    // This user has never been seen before.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username +
			   string(" User ") + name +
			   string(" has never been seen before."));
    (toSend->mutex).unlock();
    return true;
  }
  
  // Calculate the time ago sene.
  secondsDifference = currTime - seenTime;
  daysAgo  = secondsDifference / secondsInDay;

  secondsDifference -= daysAgo * secondsInDay;
  hoursAgo = secondsDifference / secondsInHour;
  
  secondsDifference -= hoursAgo * secondsInHour;
  minutesAgo = secondsDifference / secondsInMinute;
  
  // Put them in string form.
  sprintf(tempStr, "%d", daysAgo);
  daysAgoStr = string(tempStr);
  sprintf(tempStr, "%d", hoursAgo);
  hoursAgoStr = string(tempStr);
  sprintf(tempStr, "%d", minutesAgo);
  minutesAgoStr = string(tempStr);

  
  // Give him our results.
  string tosay = string("/w ") + ev.username + " " + name + " was last seen ";
  
  if(daysAgo != 0) {
    // Output the days, hours, & minutes ago.
    tosay = tosay + daysAgoStr + " days, " + 
      hoursAgoStr + " hours, " + minutesAgoStr + " minutes ago on ";
  }
  else {
    if(hoursAgo != 0) {
      // Output the hours & minutes ago only.
      tosay = tosay + hoursAgoStr + " hours, " + minutesAgoStr
	+ string(" minutes ago on ");
    }
    else {
      // Output the minutes ago only.
      tosay = tosay + minutesAgoStr + string(" minutes ago on ");
    }
  }

  // I have to do this hackish thing because apparently the asctime function
  //   returns a weird non-printable character at the end 0x0a.  My only
  //   get-around is to extract what I want from it piece by piece.
  string day, month, date, time, year;
  string temp = string(asctime(timeinfo));  // Our start string.
  istringstream currLine(temp.c_str());
  currLine >> day;
  currLine >> month;
  currLine >> date;
  currLine >> time;
  currLine >> year;

  string gayHack = day + " " + month + " " + date + " " +
    time + " " + year;


  // Send it now.
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( tosay + gayHack + string("."));
  (toSend->mutex).unlock();


  return true;
}


bool bot::cmd_love(const Event ev, conGuy *myConGuy, const string name) {
  sendQueue *toSend;
  string tosay;
  int toss = rand() % 3;

  if(name == "")
    return cmd_help(ev, myConGuy, "help", "love");

  if(toss == 0)
    tosay = ev.username + " thinks " + name + " is sheexy.";
  else if(toss == 1)
    tosay = ev.username + " touches " + name + "'s peepee!  *_*";
  else if(toss == 2)
    tosay = ev.username + " wants " + name + " to 'play' with him/her.  ;)";
  
  // Send it now.
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( tosay);
  (toSend->mutex).unlock();
  return true;
}

bool bot::cmd_smackdown(const Event ev, conGuy *myConGuy, 
			const string name) {
  sendQueue *toSend;
  string tosay;
  int toss = rand() % 5;

  if(name == "")
    return cmd_help(ev, myConGuy, "help", "smackdown");

  if(toss == 0)
    tosay = ev.username + " throws a massive SMACKDOWN on " + name + "!!!!";
  else if(toss == 1)
    tosay = ev.username + " throws a monster MAMA SMACKDOWN on " + 
      name + "!!!!!";
  else if(toss == 2)
    tosay = ev.username + string(" farts in ") + name + "'s face! OWNED!";
  else if(toss == 3)
    tosay = ev.username + " just took a dump in your yard.  SUCKkaaa!";
  else if(toss == 4)
    tosay = ev.username + " kicked " + name + " where it hurts!  Pain!";
  
  
  // Send it now.
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( tosay);
  (toSend->mutex).unlock();
  return true;
}


bool bot::cmd_ban(const Event ev, conGuy *myConGuy, const string name,
		  const string message) {
  sendQueue *toSend;
  
  if(name == "")
    return cmd_help(ev, myConGuy, "help", "ban");

  // Administrators can't be banned.  
  if(userIsAdministrator(name) == true) {
    // Let the user know.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + string(" Sorry, ") +
			   string("administrators can't be banned.  ") + 
			   string("They are GODS!"));
    (toSend->mutex).unlock();
    return true;
  }
  else {
    // Else he/she is bannable.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    // Send the ban command in chat.
    toSend->out.push_back( string("/kick ") + name);
    // Send the banned user a message if one was specified.
    if(message != "")
      toSend->out.push_back( string("/w ") + name + string(" ") + message);
    (toSend->mutex).unlock();
    // Do the actual banning in the SQLite database.
    if(! banUser(name)) {
      botlog << "ERROR:  Failed attempt to ban user:  " << name << endl;
    }
    return true;
  }
  return true;
}

bool bot::cmd_unban(const Event ev, conGuy *myConGuy, const string name) {
  sendQueue *toSend;
  
  if(name == "")
    return cmd_help(ev, myConGuy, "help", "unban");

  if(userIsBanned(name) == false) {
    // Uhh, this user isn't banned... Let the person know.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back(string("/w ") + ev.username + string(" User ") +
			  name + string(" wasn't banned in the first place!"));
    (toSend->mutex).unlock();
  }
  else {
    // Let's unban now.
    // Send the actual unban command.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    // We aren't 'banning' on bnet.
    //    toSend->out.push_back( string("/unban ") + name);
    // Let the calling user know the user is unbanned.
    toSend->out.push_back( string("/w ") + ev.username + string(" User ") +
			   name + string(" unbanned."));
    (toSend->mutex).unlock();

    // Update the database to reflect unbanned-ness.
    unBanUser(name);
  }
  return true;
}

bool bot::cmd_kick(const Event ev, conGuy *myConGuy, const string name, 
		   const string message) {
  sendQueue *toSend;

  if(userIsAdministrator(name) == true) {
    // Sorry, admins can't be kicked.  Let the user know.
    toSend = myConGuy->getSendQueue();
    (toSend->mutex).lock();
    toSend->out.push_back( string("/w ") + ev.username + string(" Sorry, ") +
			   string("administrators can't be kicked.  ") + 
			   string("They are GODS!"));
    (toSend->mutex).unlock();
  }
  else {
  // Send it now.
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( string("/kick ") + name);
  if(message != "")
    toSend->out.push_back( string("/w ") + name + " " + message);
  (toSend->mutex).unlock();
  }
  return true; 
}


bool bot::cmd_uptime(const Event ev, conGuy *myConGuy) {
  sendQueue *toSend;
  char tempStr[128];
  QDateTime currTime    = QDateTime::currentDateTime();
  QDateTime connectTime = myConGuy->getStartTime();
  
  long secondsDifference;
  int daysAgo;
  int hoursAgo;
  int minutesAgo;
  string daysAgoStr;
  string hoursAgoStr;
  string minutesAgoStr;

  const int secondsInDay  = 60 * 60 * 24;
  const int secondsInHour = 60 * 60;
  const int secondsInMinute = 60;

  
  if(connectTime.toTime_t() == 0) {
    // Strange...
    if(myConGuy->isConnected()) {
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username +
			     string(" Bot has 0 uptime."));
      (toSend->mutex).unlock();
    }
    return true;
  }
  
  // Calculate the time ago seen.
  secondsDifference = currTime.toTime_t() - connectTime.toTime_t();
  daysAgo  = secondsDifference / secondsInDay;

  secondsDifference -= daysAgo * secondsInDay;
  hoursAgo = secondsDifference / secondsInHour;
  
  secondsDifference -= hoursAgo * secondsInHour;
  minutesAgo = secondsDifference / secondsInMinute;
  
  // Put them in string form.
  sprintf(tempStr, "%d", daysAgo);
  daysAgoStr = string(tempStr);
  sprintf(tempStr, "%d", hoursAgo);
  hoursAgoStr = string(tempStr);
  sprintf(tempStr, "%d", minutesAgo);
  minutesAgoStr = string(tempStr);

  
  // Give him our results.
  string tosay = string("/w ") + ev.username + " " +
    myConGuy->username() + string(" has been online for ");
  
  if(daysAgo != 0) {
    // Output the days, hours, & minutes ago.
    tosay = tosay + daysAgoStr + " days, " + 
      hoursAgoStr + " hours, " + minutesAgoStr + " minutes.  " +
      myConGuy->username() + string(" start on ");
  }
  else {
    if(hoursAgo != 0) {
      // Output the hours & minutes ago only.
      tosay = tosay + hoursAgoStr + " hours, " + minutesAgoStr
	+ string(" minutes.  ") + myConGuy->username() + string(" start on ");
    }
    else {
      // Output the minutes ago only.
      tosay = tosay + minutesAgoStr + string(" minutes.  ") + 
	myConGuy->username() + string(" start on ");
    }
  }

  tosay += string(connectTime.toString().ascii());

  // Send it now.
  toSend = myConGuy->getSendQueue();
  (toSend->mutex).lock();
  toSend->out.push_back( tosay );
  (toSend->mutex).unlock();

  return true;
}

// Connect a gateway that is not connected.
bool bot::cmd_connect(const Event ev, conGuy *myConGuy, const string gateway) {
  sendQueue *toSend;  // Used to let the user know if the conGuy is already
                      //   connected.
  // For timestamp in the logging.
  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );


  if(gateway == "useast") {
    if(useast->isConnected() == true) {
      // Let the user know the conGuy is already connected.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + 
			     string(" That gateway is already connected!"));
      (toSend->mutex).unlock();      
    }
    else {
      // Connect the damn thing already!
      botlog << "Received command for the connect of gateway " << gateway
	     << " at " << string(asctime(timeinfo)) << endl;
      useast->disconnectNow();   // Disconnect first just in case...
      useast->connectNow("useast");
    }
  }
  else if(gateway == "uswest") {
    if(uswest->isConnected() == true) {
      // Let the user know the conGuy is already connected.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + 
			     string(" That gateway is already connected!"));
      (toSend->mutex).unlock();      
    }
    else {
      // Connect the damn thing already!
      botlog << "Received command for the connect of gateway " << gateway
	     << " at " << string(asctime(timeinfo)) << endl;
      uswest->disconnectNow();   // Disconnect first just in case...
      uswest->connectNow("uswest");
    }
  }
  else if(gateway == "europe") {
    if(europe->isConnected() == true) {
      // Let the user know the conGuy is already connected.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + 
			     string(" That gateway is already connected!"));
      (toSend->mutex).unlock();      
    }
    else {
      // Connect the damn thing already!
      botlog << "Received command for the connect of gateway " << gateway
	     << " at " << string(asctime(timeinfo)) << endl;
      europe->disconnectNow();   // Disconnect first just in case...
      europe->connectNow("europe");
    }
  }
  else if(gateway == "asia") {
    if(asia->isConnected() == true) {
      // Let the user know the conGuy is already connected.
      toSend = myConGuy->getSendQueue();
      (toSend->mutex).lock();
      toSend->out.push_back( string("/w ") + ev.username + 
			     string(" That gateway is already connected!"));
      (toSend->mutex).unlock();      
    }
    else {
      // Connect the damn thing already!
      botlog << "Received command for the connect of gateway " << gateway
	     << " at " << string(asctime(timeinfo)) << endl;
      asia->disconnectNow();   // Disconnect first just in case...
      asia->connectNow("asia");
    }
  }
  else {
    // The user didn't specify the gateway explicitly (or invalid gateway).
    cmd_help(ev, myConGuy, "help", "connect");
  }
  return true;
}

bool bot::cmd_reconnect(const Event ev, conGuy *myConGuy, 
			const string gateway) {
  // For timestamp in the logging.
  time_t rawtime;
  struct tm * timeinfo;

  // To get rid of the warnings that ev and myConGuy are not used.  It
  // is a parameter just for a consistent function interface.
  string dummy;
  dummy = ev.username;
  conGuy* dummy2;
  dummy2 = myConGuy;


  time ( &rawtime );
  timeinfo = localtime ( &rawtime );


  // If the gateway is not explicitly specified, treat it as reconnecting all
  //   of the gateways.
  if(gateway != "useast" && 
     gateway != "uswest" &&
     gateway != "europe" && 
     gateway != "asia") {

    botlog << "Received command for reconnect of all gateways at "
	   << string(asctime(timeinfo)) << endl;

    reExecFlag = true;
    useast->disconnectNow();
    uswest->disconnectNow();
    europe->disconnectNow();
    asia->disconnectNow();
    bnet->done = true;
  }
  else if(gateway == "useast") {
    botlog << "Received command for the reconnect of gateway " << gateway
	   << " at " << string(asctime(timeinfo)) << endl;
    useast->disconnectNow();
    sleep(2);  // Just in case bnet doesn't like quick disconnects & reconnects
    
    useast->connectNow("useast");
  }
  else if(gateway == "uswest") {
    botlog << "Received command for the reconnect of gateway " << gateway
	   << " at " << string(asctime(timeinfo)) << endl;
    uswest->disconnectNow();
    sleep(2);  // Just in case bnet doesn't like quick disconnects & reconnects
    uswest->connectNow("uswest");
  }
  else if(gateway == "europe") {
    botlog << "Received command for the reconnect of gateway " << gateway
	   << " at " << string(asctime(timeinfo)) << endl;
    europe->disconnectNow();
    sleep(2);  // Just in case bnet doesn't like quick disconnects & reconnects
    europe->connectNow("europe");
  }
  else if(gateway == "asia") {
    botlog << "Received command for the reconnect of gateway " << gateway
	   << " at " << string(asctime(timeinfo)) << endl;
    asia->disconnectNow();
    sleep(2);  // Just in case bnet doesn't like quick disconnects & reconnects
    asia->connectNow("asia");
  }
  
  return true;
}


bool bot::cmd_disconnect(const Event ev, conGuy *myConGuy, 
			 const string gateway) {
  
  // The gateway MUST be specified in order to use this command.
  if(gateway == "useast")
    useast->disconnectNow();
  else if(gateway == "uswest")
    uswest->disconnectNow();
  else if(gateway == "europe")
    europe->disconnectNow();
  else if(gateway == "asia")
    asia->disconnectNow();
  else {
    // The user obviously doesn't know how to use this command...
    //   (Either no gateway specified or bad gateway specified)
    return cmd_help(ev, myConGuy, "help", "disconnect");
  }

  // If it got here, the gateway specified was 'good'.  
  // Log that the disconnect happened.

  // For timestamp.
  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  botlog << "Received command for the disconnect of gateway " << gateway
	 << " at " << string(asctime(timeinfo)) << endl;
  
  return true;
}


bool bot::cmd_exit(const Event ev, conGuy *myConGuy) {
  // For timestamp.
  time_t rawtime;
  struct tm * timeinfo;

  // To get rid of the warnings that ev and myConGuy are not used.  It
  // is a parameter just for a consistent function interface.
  string dummy;
  dummy = ev.username;
  conGuy* dummy2;
  dummy2 = myConGuy;

  time ( &rawtime );
  timeinfo = localtime ( &rawtime );

  botlog << "Received command for exit at " << string(asctime(timeinfo)) 
	 << endl;

  reExecFlag = false;
  useast->disconnectNow();
  uswest->disconnectNow();
  europe->disconnectNow();
  asia->disconnectNow();
  bnet->done = true;
  return true;
}



bool bot::open_sql_db() {
  int rc;
  char *err = 0;  // Where the error message will be stored.
  // Open the database file.
  db = sqlite_open(BOT_SQLITE_DB, 0, &err);
  if( db==0 ){
    botlog << "ERROR:  Can't open database: " << string(err) << endl;
    free(err); err = 0;
    return false;
  }

  // Create the users table if it doesn't exist.
  rc = sqlite_exec( db, "select * from users;", 0, 0, &err);
  if((rc != SQLITE_OK) && (err != 0) && 
     (string(err) == "no such table: users")) {
    free(err);
    err = 0;
    rc = sqlite_exec( db, "create table users (user varchar(32), time number(10), banned number(1), guest number(1), regular number(1), operator number(1), administrator number(1), havemail number(1));", 0, 0, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "ERROR:  SQL:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }

  // Create the botmails table if it doesn't exist.
  rc = sqlite_exec( db, "select * from botmails;", 0, 0, &err);
  if((rc != SQLITE_OK) && (err != 0) && 
     (string(err) == "no such table: botmails")) {
    free(err);
    err = 0;
    rc = sqlite_exec( db, "create table botmails (touser varchar(32), fromuser varchar(32), time number(10), message varchar(255));", 0, 0, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "ERROR:  SQL:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }

  // Create the allseens table if it doesn't exist.

  rc = sqlite_exec( db, "select * from allseens;", 0, 0, &err);
  if((rc != SQLITE_OK) && (err != 0) && 
     (string(err) == "no such table: allseens")) {
    free(err);
    err = 0;
    int j = 0;  // counter variable for the allseen column names below.
    rc = sqlite_exec_printf( db, 
			     string( string("create table allseens (") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32), ") +
				     string("user%d varchar(32));")).c_str(),
			     0, 0, &err,
			     j++, j++, j++, j++, j++, j++, j++, j++, j++, j++, 
			     j++, j++, j++, j++, j++, j++, j++, j++, j++, j++);

    if(rc != SQLITE_OK && err != 0) {
      botlog << "ERROR:  SQL:  " << string(err) << endl;
      free(err);
      err = 0;
    }
    else {
        Q_ASSERT(j == 20);
    }
    // Let's set all entries in the allseens table to ""'s.
    j = 0;
    rc = sqlite_exec_printf(db, 
			    string(string("insert into allseens (") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d, ") +
				   string("user%d, ") + string("user%d) ") +
				   string("values (") +
				   string("'', '', '', '', '', '', '', '', ") +
				   string("'', '', '', '', '', '', '', '', ") +
				   string("'', '', '', '');")).c_str(),
			     0, 0, &err,
			     j++, j++, j++, j++, j++, j++, j++, j++, j++, j++, 
			     j++, j++, j++, j++, j++, j++, j++, j++, j++, j++);


    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
  } // end of if there is no allseens table.
  return true;
}


bool bot::update_user(string username) {
  int rc;
  int i = 0;
  time_t currTime;
  char *err;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  
  list<string> allseens;

  // First convert the username to lowercase.
  for(i = 0; i < static_cast<int>(username.size()); i++)
    username[i] = tolower(username[i]);


  // If the user doesn't exist, create it.  We also have to check if
  //   he has any mails in our botmails table.
  if(! user_exists(username)) {
    // Check to see if there were any botmails queued for him.
    bool haveMail = false;
    
    // Make the exeStr
    exeStr = sqlite_mprintf("select * from botmails where touser = %Q;",
			    username.c_str());

    // Compile the sql exeStr.
    rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }

    // Step through the results by row.  If we have even one, 
    //   then set haveMail = true;
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
    else if(rc == SQLITE_DONE) {
      haveMail = false;
    }
    else if(rc == SQLITE_ROW) {
      haveMail = true;
    }
    // Finalize the virtual machine.  (cleanup)
    rc = sqlite_finalize(virtualMachine, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
    if(exeStr != 0) {
      sqlite_freemem(exeStr);
      exeStr = 0;
    }
    if(err != 0) {
      free(err);
      err = 0;
    }
    // Create the user with whatever mail status he has.
    create_new_user(username, haveMail);
  }


  // Update the users table for the username's time entry.
  time ( &currTime );
  rc = sqlite_exec_printf( db, 
			   string( string("update users set time = %d ") +
				   string("where user = %Q;")).c_str(),
			   0, 0, &err,
			   currTime, username.c_str());
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Update the users table for the haveMail entry.  Sync it with the botmails
  //   table.
    // Make the exeStr
    exeStr = sqlite_mprintf("select * from botmails where touser = %Q;",
			    username.c_str());

    // Compile the sql exeStr.
    rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }

    // Step through the results by row.  
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
    else if(rc == SQLITE_DONE) {
      // We have no botmails.  Set the haveMail in the users table to 0.
      rc = sqlite_exec_printf( db, 
			       string( string("update users set havemail = 0")+
				       string(" where user = %Q;")).c_str(),
			       0, 0, &err,
			       username.c_str());
      if(rc != SQLITE_OK && err != 0) {
	botlog << "SQL ERROR:  " << string(err) << endl;
	free(err);
	err = 0;
	return false;
      }
    }
    else if(rc == SQLITE_ROW) {
      // We have at least 1 botmail.  Set the haveMail in the users table to 1.
      rc = sqlite_exec_printf( db, 
			       string( string("update users set havemail = 1")+
				       string(" where user = %Q;")).c_str(),
			       0, 0, &err,
			       username.c_str());
      if(rc != SQLITE_OK && err != 0) {
	botlog << "SQL ERROR:  " << string(err) << endl;
	free(err);
	err = 0;
	return false;
      }
    }
    // Finalize the virtual machine.  (cleanup)
    rc = sqlite_finalize(virtualMachine, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
    if(exeStr != 0) {
      sqlite_freemem(exeStr);
      exeStr = 0;
    }
    if(err != 0) {
      free(err);
      err = 0;
    }

  
  
  // Now update the allseens table.
  
  // Find out if the user is already in the allseens table.  If yes,
  //   shift everything to the right of it over.

  // Make the exeStr
  exeStr = sqlite_mprintf("select * from allseens;");

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  else if(rc == SQLITE_DONE) {
    // sqlite_step() is finished executing.

    // It shouldn't get in here because the allseens table always has
    //   something in it.
  }
  else if(rc == SQLITE_ROW) {
    // Good, precisely what we're looking for.
    // Store a copy of the columnData in a list since it should only have 1 row
    
    for(i = 0; i < numColumns; i++)
      allseens.push_back( string(columnData[i]));

    // Now close up, so we can edit the database again.

    // Finalize the virtual machine.  (cleanup)
    rc = sqlite_finalize(virtualMachine, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }

    if(exeStr != 0) {
      sqlite_freemem(exeStr);
      exeStr = 0;
    }
    if(err != 0) {
      free(err);
      err = 0;
    }

    for(list<string>::reverse_iterator riter1 = allseens.rbegin();
	riter1 != allseens.rend(); riter1++) {
      if( username == *riter1) {
	// If it got here, we have this user already in the allseens table.
	// Shift everything to the left of him to the right.  Last spot
	//   (user19) is set to blank.  (there's no user20 spot!)

	// Let's create a second iterator.  riter1 will stay one spot ahead of
	//   riter2.  (incrementing both).
	int i = 0;
	for(list<string>::reverse_iterator riter2 = riter1++;
	    riter2 != allseens.rend(); riter2++, riter1++, i++) {
	  const char *currUsername;   // Points to the current username.
	  char nullChar = '\0'; // Null character; used for spot user19.

	  // Use the nullChar if it's the last spot, otherwise use the
	  //   actual columnData username.
	  if(riter1 == allseens.rend())
	    currUsername = &nullChar;
	  else
	    currUsername = (*riter1).c_str();

	  *riter2 = string(currUsername);
	  // Update!
	  rc = sqlite_exec_printf( db,
				   string( string("update allseens ") + 
					   string("set user%d = %Q;")).c_str(),
				   0, 0, &err,
				   i, currUsername);
	  if(rc != SQLITE_OK && err != 0) {
	    botlog << "SQL ERROR:  " << string(err) << endl;
	    free(err);
	    err = 0;
	  }
	}
	// We're done with this part, break out of the for loop.
	break;
      }  // end of  if( username == *riter1)
    }  // end of  FOR loop.
  } // end of   else if(rc == SQLITE_ROW)

  

  // Now, shift everything 1 spot to the left to
  //   make room for this user at the spot user0

  // Start from the left and move rightwards so we don't clobber stuff.
  i = allseens.size()-1;
  for(list<string>::iterator iter = ++(allseens.begin());
      iter != allseens.end(); iter++, i--) {

    const char* currUsername = (*iter).c_str();
    
    rc = sqlite_exec_printf( db,
			     string( string("update allseens ") + 
				     string("set user%d = %Q;")).c_str(),
			     0, 0, &err,
			     i, currUsername);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
  }

  
  // We're now done shifting all the crap around.  user0 spot is ready
  //   for insertion.
      // Update!
  rc = sqlite_exec_printf( db,
			   string( string("update allseens ") + 
				   string("set user0 = %Q;")).c_str(),
			   0, 0, &err, username.c_str());
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  return true;
}  // end of bool update_user(string username)

bool bot::has_mail(string username) {
  bool rv;
  int rc;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);


  
  // Make the exeStr
  exeStr = sqlite_mprintf("select havemail from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  
  // If we have a row (user exists), check it.
  if(rc == SQLITE_ROW) {
    // There should only be one column returned.
    if( atoi(columnData[0]) == 0)
      rv = false;
    else
      rv = true;
  }
  else {
    rv = false;
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
} // end of   bool has_mail(string username)


bool bot::user_exists(string username) {
  bool rv;
  int rc;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select user from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  
  // If we have a row (user exists), return value = true; else false.
  if(rc == SQLITE_ROW) {
    rv = true;
  }
  else {
    rv = false;
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
}


// creates a new user with the time_t == the current time.
bool bot::create_new_user(string username, bool havemail) {
  time_t currTime;
  time ( &currTime );

  return create_new_user(username, havemail, currTime);
}

// creates a new user with the time_t == the seentime parameter.
bool bot::create_new_user(string username, bool havemail, time_t seentime) {
  int rc;
  char *err;
  int intHasMail;

  if(havemail == true)
    intHasMail = 1;
  else
    intHasMail = 0;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  rc = sqlite_exec_printf( db, 
			   string( string("insert into users (user, ") +
				   string("time, banned, guest, ") +
				   string("regular, operator, ") +
				   string("administrator, havemail) ") +
				   string("values (%Q, %d, 0, 1, 0, 0, 0, ") +
				   string("%d);")).c_str(),
			   0, 0, &err, 
			   username.c_str(), seentime, intHasMail);

  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }
  else {
    return true;
  }
}

bool bot::queue_mail(string userfrom, string userto, string message) {
  int rc;
  unsigned int i = 0;
  char *err = 0;
  time_t currTime;
  time ( &currTime );

  // If it's an empty message, don't queue any mail.
  if(message == "")
    return false;

  // First convert the usernames to lowercase.
  for(i = 0; i < userfrom.size(); i++)
    userfrom[i] = tolower(userfrom[i]);
  for(i = 0; i < userto.size(); i++)
    userto[i] = tolower(userto[i]);

  // If the from-user or the to-user doesn't exist, create him/her with 
  //   last seen time of 0. (1970).
  if( ! user_exists(userfrom))
    create_new_user(userfrom, false, 0);
  if( ! user_exists(userto))
    create_new_user(userto, true, 0);

  // Insert a botmail.
  rc = sqlite_exec_printf( db, 
			   string( string("insert into botmails (touser, ") +
				   string("fromuser, time, message) ") +
				   string("values (%Q, %Q, %d, %Q);")).c_str(),
			   0, 0, &err, 
			   userto.c_str(), userfrom.c_str(), currTime,
			   message.c_str());
  
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Set the touser to havemail == true.
  rc = sqlite_exec_printf( db, 
			   string( string("update users set havemail = 1 ") +
				   string("where user = %Q;")).c_str(),
			   0, 0, &err,
			   userto.c_str());
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }
  return true;
}



bool bot::mail_ops(string userfrom, string message) {
  int rc;
  bool rv = false;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;
  
  list<string> allOperators;

  // First convert the userfrom to lowercase.
  for(unsigned int i = 0; i < userfrom.size(); i++)
    userfrom[i] = tolower(userfrom[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select user from users where operator = 1;");

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Step through the results by row.
  rc = SQLITE_ROW;
  while(rc == SQLITE_ROW) {
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      rv = false;
    }
    else if(rc == SQLITE_ROW) {
      rv = true;
      // push_back some stuff onto 'allOperators' list.
      allOperators.push_back( string(columnData[0]));
    }
    else if(rc == SQLITE_DONE) {
      // There are no ops!
      rv = true;
    }
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  // If it died somewhere along the way, return false;
  if(rv == false)
    return false;

  // Queue mail to each operator.
  for(list<string>::iterator iter = allOperators.begin();
      iter != allOperators.end(); iter++) {

    queue_mail(userfrom, *iter, message);
  }
  return true;
}


// This function returns the oldest botmail (userfrom, queueTime, and msg) 
//   that was queued if the return value is true.  It does not remove
//   the botmail at the end anymore.  Use remove_mail for that.
bool bot::check_mail(string userto, string &userfrom, time_t &queueTime, 
		string &msg) {
  int rc;
  bool rv = true;
  char *err;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  
  list<time_t> mailTimes;

  // First convert the userto to lowercase.
  for(unsigned int i = 0; i < userto.size(); i++)
    userto[i] = tolower(userto[i]);

  // Make sure the user exists in our database and that he/she really has mail.
  if(! user_exists(userto))
    return false;
  if(! has_mail(userto))
    return false;

  // Ok, here's how we're going to do this.  First post a 'SELECT' request
  //   to the sql, requesting for the time_t time that matches the userto
  //   and the userfrom.  Find the lowest time_t and do another request
  //   again (matching all parameters this time) but get all data.
  //   We will store this info into the &parameters.  Then remove the
  //   row/entry.  Return true.
  //   


  // Make the exeStr
  exeStr = sqlite_mprintf("select time from botmails where touser = %Q;",
			  userto.c_str());
  
  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    if(exeStr != 0) {
      sqlite_freemem(exeStr);
      exeStr = 0;
    }
    return false;
  }


  rc = SQLITE_ROW;  // Just to get started in the while loop.
  while(rc == SQLITE_ROW) {
    // Step through the results by row.
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      // Finalize the virtual machine.  (cleanup)
      rc = sqlite_finalize(virtualMachine, &err);
      if(rc != SQLITE_OK && err != 0) {
	botlog << "SQL ERROR:  " << string(err) << endl;
	free(err);
	err = 0;
      }
      if(exeStr != 0) {
	sqlite_freemem(exeStr);
	exeStr = 0;
      }
      if(err != 0) {
	free(err);
	err = 0;
      }
      return false;
    }
    else if(rc == SQLITE_DONE) {
      // We're finished.  We have all the mail times for this user.
      //   This is our last time through the while.
    }
    else if(rc == SQLITE_ROW) {
      // Good, precisely what we're looking for.
      // Store a copy of the each botmail's time in a list so we can analyze.
      mailTimes.push_back( (time_t)atoi(columnData[0]) );
    }
  }

  
  // If mailTimes.size() == 1, well, that means that there's only one mail
  //   for this guy.  We'll set the havemail flag in the 'users' table
  //   to 0.
  if(mailTimes.size() == 1) {
    rc = sqlite_exec_printf( db, 
			     string( string("update users set havemail = 0 ") +
				     string("where user = %Q;")).c_str(),
			     0, 0, &err,
			     userto.c_str());
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }
  
  // Find what the lowest mail time is.
  time_t lowest = LONG_MAX;
  if(mailTimes.size() != 0) {
    for(list<time_t>::iterator iter = mailTimes.begin();
	iter != mailTimes.end(); iter++) {
    
      if(*iter < lowest)
	lowest = *iter;
    }
    if(lowest == LONG_MAX) {
      rv = false;
    }
  }
  else {
    rv = false;
  }
  // Now close up, so we can edit the database again (if rv still == true)

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }
  
  // Return if it got an error somewhere along the line.
  if(rv == false)
    return false;


  // Now we will retreive the oldest piece of mail. (at time_t 'lowest').
  // Make the exeStr.
  exeStr = sqlite_mprintf("select * from botmails where touser = %Q and "
			  "time = %d;",
			  userto.c_str(), lowest);
  
  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    if(exeStr != 0) {
      sqlite_freemem(exeStr);
      exeStr = 0;
    }
    return false;
  }

  // Now step through the results.  There should be only 1 step required.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = false;
  }
  else if(rc == SQLITE_DONE) {
    botlog << "SQL ERROR:  bah! " << __FILE__ << "(" << __LINE__ << ")" << endl;
    rv = false;
  }
  else if(rc == SQLITE_ROW) {
    // Good, we have the one piece of data we wanted.  Get it.
    for(int i = 0; i < numColumns; i++) {
      if(string(columnNames[i]) == "fromuser")
	userfrom = string(columnData[i]);
      else if(string(columnNames[i]) == "time")
	queueTime = atoi(columnData[i]);
      else if(string(columnNames[i]) == "message")
	msg = string(columnData[i]);
    }
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  // Exit out if it failed somewhere along the way.
  if(rv == false)
    return false;
  else 
    return true;
}

// This function removes a botmail from the database using just
// userfrom and queueTime.  (overloaded function.)
bool bot::remove_mail(string userfrom, time_t queueTime) {
  int rc;
  char *err = 0;
  bool rv = true;
  string userto;
  // First convert the username to lowercase.
  for(unsigned int i = 0; i < userfrom.size(); i++)
    userfrom[i] = tolower(userfrom[i]);


  // Check to see if mail exists for the parameters in the first place.

  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // Make the exeStr
  exeStr = sqlite_mprintf("select * from botmails where "
			  "fromuser = %Q and time = %d;",
			   userfrom.c_str(), queueTime);
			  
  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = false;
  }
  if(rc == SQLITE_DONE) {
    // Dude, we didn't find a botmail for those parameters.  Can't delete
    // a nonexistant botmail.
    rv = false;
  }
  else if(rc == SQLITE_ROW) {
    // Set the userto string.
    userto = string(columnData[0]);
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  // If we know there isn't even an entry, just return false now.
  if(rv == false)
    return false;


  // Actually delete the botmail.
  rc = sqlite_exec_printf( db, "delete from botmails where "
			   "fromuser = %Q and time = %d;",
			   0, 0, &err,
			   userfrom.c_str(), queueTime);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = false;
  }

  // If the userto for that botmail has no other botmails to him/her,
  // reset the haveMail flag in the users table.
  // Make the exeStr
  exeStr = sqlite_mprintf("select * from botmails where "
			  "touser = %Q;",
			   userto.c_str());
			  
  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = false;
  }
  if(rc == SQLITE_DONE) {
    // The userto doesn't have any other botmails.

    // Set the touser to havemail == false
    rc = sqlite_exec_printf( db, 
			     string( string("update users set havemail = 0 ") +
				     string("where user = %Q;")).c_str(),
			     0, 0, &err,
			     userto.c_str());
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }
  else if(rc == SQLITE_ROW) {
    // The userto has other botmails, no need to update the users table,
    //   but we'll do it anyways, just in case for consistency.
    //   Set the touser to havemail == true
    rc = sqlite_exec_printf( db, 
			     string( string("update users set havemail = 1 ") +
				     string("where user = %Q;")).c_str(),
			     0, 0, &err,
			     userto.c_str());
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  
  return rv;
}


// This function removes a botmail from the database using
// the userfrom, userto, and queueTime.  (overloaded function.)
bool bot::remove_mail(string userfrom, string userto, time_t queueTime) {
  int rc;
  unsigned int i = 0;
  char *err = 0;
  bool rv = true;
  // First convert the usernames to lowercase.
  for(i = 0; i < userfrom.size(); i++)
    userfrom[i] = tolower(userfrom[i]);
  for(i = 0; i < userto.size(); i++)
    userto[i] = tolower(userto[i]);


  // Check to see if mail exists for the parameters in the first place.

  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // Make the exeStr
  exeStr = sqlite_mprintf("select * from botmails where "
			  "touser = %Q and fromuser = %Q and time = %d;",
			   userto.c_str(), userfrom.c_str(), queueTime);
			  
  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = false;
  }
  if(rc == SQLITE_DONE) {
    // Dude, we didn't find a botmail for those parameters.  Can't delete
    // a nonexistant botmail.
    rv = false;
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  // If we know there isn't even an entry, just return false now.
  if(rv == false)
    return false;


  // Actually delete the botmail.
  rc = sqlite_exec_printf( db, "delete from botmails where "
			   "touser = %Q and fromuser = %Q and time = %d;",
			   0, 0, &err,
			   userto.c_str(), userfrom.c_str(), queueTime);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = false;
  }


  // If the userto for that botmail has no other botmails to him/her,
  // reset the haveMail flag in the users table.
  // Make the exeStr
  exeStr = sqlite_mprintf("select * from botmails where "
			  "touser = %Q;",
			   userto.c_str());
			  
  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = false;
  }
  if(rc == SQLITE_DONE) {
    // The userto doesn't have any other botmails.
    // Set the touser to havemail == false
    rc = sqlite_exec_printf( db, 
			     string( string("update users set havemail = 0 ") +
				     string("where user = %Q;")).c_str(),
			     0, 0, &err,
			     userto.c_str());
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }

  }
  else if(rc == SQLITE_ROW) {
    // The userto has other botmails, no need to update the users table,
    //   but we'll do it anyways, just in case for consistency.
    //   Set the touser to havemail == true
    rc = sqlite_exec_printf( db, 
			     string( string("update users set havemail = 1 ") +
				     string("where user = %Q;")).c_str(),
			     0, 0, &err,
			     userto.c_str());
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }

  return rv;
}

// What we are doing here is pretty much returning the allseens table.
bool bot::allseen(list<string> &allseenList, int number) {
  int rc;
  bool rv;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First clear the list before we start.
  allseenList.clear();

  // Make the exeStr
  exeStr = sqlite_mprintf("select * from allseens;");

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  if(rc == SQLITE_ROW) {
    rv = true;
    // Making sure we aren't out of bounds...
    if(number > numColumns)
      number = numColumns;
    
    for(int i = numColumns-1; i >= numColumns-number; i--) {
      if(string(columnData[i]) != "")
	allseenList.push_back(string(columnData[i]));
    }
  }
  else {
    rv = false;
  }
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
} // end of    bool allseen(int number)



bool bot::banned(list<string> &bannedList) {
  int rc;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First clear the list before we start.
  bannedList.clear();

  // Make the exeStr
  exeStr = sqlite_mprintf("select user from users where banned = 1;");

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  rc = SQLITE_ROW;  // Just to get started in the while loop.
  while(rc == SQLITE_ROW) {
    // Step through the results by row.
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      // Finalize the virtual machine.  (cleanup)
      rc = sqlite_finalize(virtualMachine, &err);
      if(rc != SQLITE_OK && err != 0) {
	botlog << "SQL ERROR:  " << string(err) << endl;
	free(err);
	err = 0;
      }
      if(exeStr != 0) {
	sqlite_freemem(exeStr);
	exeStr = 0;
      }
      if(err != 0) {
	free(err);
	err = 0;
      }
      return false;
    }
    else if(rc == SQLITE_DONE) {
      // We're finished.  We have all the banned users.
    }
    else if(rc == SQLITE_ROW) {
      // Good, precisely what we're looking for.
      bannedList.push_back( string(columnData[0]));
    }
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }
  return true;  
}
bool bot::operators(list<string> &operatorsList) {
  int rc;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First clear the list before we start.
  operatorsList.clear();

  // Make the exeStr
  exeStr = sqlite_mprintf("select user from users where operator = 1;");

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  rc = SQLITE_ROW;  // Just to get started in the while loop.
  while(rc == SQLITE_ROW) {
    // Step through the results by row.
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      // Finalize the virtual machine.  (cleanup)
      rc = sqlite_finalize(virtualMachine, &err);
      if(rc != SQLITE_OK && err != 0) {
	botlog << "SQL ERROR:  " << string(err) << endl;
	free(err);
	err = 0;
      }
      if(exeStr != 0) {
	sqlite_freemem(exeStr);
	exeStr = 0;
      }
      if(err != 0) {
	free(err);
	err = 0;
      }
      return false;
    }
    else if(rc == SQLITE_DONE) {
      // We're finished.  We have all the banned users.
    }
    else if(rc == SQLITE_ROW) {
      // Good, precisely what we're looking for.
      operatorsList.push_back( string(columnData[0]));
    }
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }
  return true;
}
bool bot::administrators(list<string> &administratorsList) {
  int rc;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First clear the list before we start.
  administratorsList.clear();

  // Make the exeStr
  exeStr = sqlite_mprintf("select user from users where administrator = 1;");

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  rc = SQLITE_ROW;  // Just to get started in the while loop.
  while(rc == SQLITE_ROW) {
    // Step through the results by row.
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      // Finalize the virtual machine.  (cleanup)
      rc = sqlite_finalize(virtualMachine, &err);
      if(rc != SQLITE_OK && err != 0) {
	botlog << "SQL ERROR:  " << string(err) << endl;
	free(err);
	err = 0;
      }
      if(exeStr != 0) {
	sqlite_freemem(exeStr);
	exeStr = 0;
      }
      if(err != 0) {
	free(err);
	err = 0;
      }
      return false;
    }
    else if(rc == SQLITE_DONE) {
      // We're finished.  We have all the banned users.
    }
    else if(rc == SQLITE_ROW) {
      // Good, precisely what we're looking for.
      administratorsList.push_back( string(columnData[0]));
    }
  }

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0) {
    sqlite_freemem(exeStr);
    exeStr = 0;
  }
  if(err != 0) {
    free(err);
    err = 0;
  }
  return true;
}



bool bot::userIsBanned(string username) {
  int rc;
  bool rv;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select banned from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  
  // If we have a row (user exists), check it.
  if(rc == SQLITE_ROW) {
    // There should only be one column returned.
    if( atoi(columnData[0]) == 0)
      rv = false;
    else
      rv = true;
  }
  else {
    rv = false;
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
}  // end of   bool userIsBanned(string username)
bool bot::userIsGuest(string username) {
  int rc;
  bool rv;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select guest from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  
  // If we have a row (user exists), check it.
  if(rc == SQLITE_ROW) {
    // There should only be one column returned.
    if( atoi(columnData[0]) == 0)
      rv = false;
    else
      rv = true;
  }
  else {
    rv = false;
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
} // end of    bool userIsGuest(string username)


bool bot::userIsRegular(string username) {
  int rc;
  bool rv;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select regular from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  
  // If we have a row (user exists), check it.
  if(rc == SQLITE_ROW) {
    // There should only be one column returned.
    if( atoi(columnData[0]) == 0)
      rv = false;
    else
      rv = true;
  }
  else {
    rv = false;
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
} // end of    bool userIsRegular(string username)

bool bot::userIsOperator(string username) {
  int rc;
  bool rv;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select operator from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  
  // If we have a row (user exists), check it.
  if(rc == SQLITE_ROW) {
    // There should only be one column returned.
    if( atoi(columnData[0]) == 0)
      rv = false;
    else
      rv = true;
  }
  else {
    rv = false;
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
} // end of    bool userIsOperator(string username)

bool bot::userIsAdministrator(string username) {
  int rc;
  bool rv;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select administrator from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }
  
  // If we have a row (user exists), check it.
  if(rc == SQLITE_ROW) {
    // There should only be one column returned.
    if( atoi(columnData[0]) == 0)
      rv = false;
    else
      rv = true;
  }
  else {
    rv = false;
  }
  
  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  return rv;
}  // end of   bool userIsAdministrator(string username)

time_t bot::seenUser(string username) {
  int rc;
  time_t rv = 0;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  //   
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  // Make the exeStr
  exeStr = sqlite_mprintf("select time from users where user = %Q;",
			  username.c_str());

  // Compile the sql exeStr.
  rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  // Step through the results by row.
  rc = sqlite_step(virtualMachine, &numColumns,
		   &columnData, &columnNames);
  if(rc != SQLITE_ROW && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    rv = 0; // At zero time (1970) if it didn't find a row.
  }
  else if(rc == SQLITE_ROW) {
    // should only be 1 column.
    rv = atoi(columnData[0]);
  }
  else {
    rv = 0;  // At zero time (1970) if it didn't find a row.
  }
  

  // Finalize the virtual machine.  (cleanup)
  rc = sqlite_finalize(virtualMachine, &err);
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
  }

  if(exeStr != 0)
    sqlite_freemem(exeStr);
  if(err != 0)
    free(err);
  
  return rv;
}

bool bot::promoteUserToAdmin(string username) {
  int rc;
  char *err = 0;
  // This is the string to execute after it's been run through the
  //   sqlite_mprintf() function.  (which malloc's memory).
  //   It needs to be passed to the sqlite_freemem() function.
  char *exeStr     = 0;
  sqlite_vm *virtualMachine = 0;
  const char *exeStrTail = 0;
  int numColumns;
  const char **columnData;
  const char **columnNames;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  if(! user_exists(username)) {
    // Create the user with the correct havemail flag.

    // Check to see if there were any botmails queued for him.
    bool haveMail = false;
    
    // Make the exeStr
    exeStr = sqlite_mprintf("select * from botmails where touser = %Q;",
			    username.c_str());

    // Compile the sql exeStr.
    rc = sqlite_compile(db, exeStr, &exeStrTail, &virtualMachine, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }

    // Step through the results by row.  If we have even one, 
    //   then set haveMail = true;
    rc = sqlite_step(virtualMachine, &numColumns,
		     &columnData, &columnNames);
    if(rc != SQLITE_ROW && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
    else if(rc == SQLITE_DONE) {
      haveMail = false;
    }
    else if(rc == SQLITE_ROW) {
      haveMail = true;
    }
    // Finalize the virtual machine.  (cleanup)
    rc = sqlite_finalize(virtualMachine, &err);
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
    }
    if(exeStr != 0) {
      sqlite_freemem(exeStr);
      exeStr = 0;
    }
    if(err != 0) {
      free(err);
      err = 0;
    }
    // Create the user with whatever mail status he has.
    create_new_user(username, haveMail);
  }

  if(! unBanUser(username))
    return false;


  // Set guest == 1
  rc = sqlite_exec_printf( db,
			   string( string("update users ") + 
				   string("set guest = 1 where ") +
				   string("user = %Q;")).c_str(),
			   0, 0, &err,
			   username.c_str());
  
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Set regular == 1
  rc = sqlite_exec_printf( db,
			   string( string("update users ") + 
				   string("set regular = 1 where ") +
				   string("user = %Q;")).c_str(),
			   0, 0, &err,
			   username.c_str());
  
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }


  // Set operator == 1
  rc = sqlite_exec_printf( db,
			   string( string("update users ") + 
				   string("set operator = 1 where ") +
				   string("user = %Q;")).c_str(),
			   0, 0, &err,
			   username.c_str());
  
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  // Set administrator == 1
  rc = sqlite_exec_printf( db,
			   string( string("update users ") + 
				   string("set administrator = 1 where ") +
				   string("user = %Q;")).c_str(),
			   0, 0, &err,
			   username.c_str());
  
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }

  return true;
}

bool bot::promoteUser(string username) {
  int rc;
  char *err = 0;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  if(! user_exists(username))
    return false;

  if( userIsAdministrator(username) || userIsOperator(username))
    return false;

  if( userIsRegular(username)) {
    // Set operator == 1
    rc = sqlite_exec_printf( db,
			     string( string("update users ") + 
				     string("set operator = 1 where ") +
				     string("user = %Q;")).c_str(),
			     0, 0, &err,
			     username.c_str());
  
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }
  else if( userIsGuest(username)) {
    // Set regular == 1
    rc = sqlite_exec_printf( db,
			     string( string("update users ") + 
				     string("set regular = 1 where ") +
				     string("user = %Q;")).c_str(),
			     0, 0, &err,
			     username.c_str());
  
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }
  return true;
}


bool bot::demoteUser(string username) {
  int rc;
  char *err = 0;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  if(! user_exists(username))
    return false;

  if( userIsAdministrator(username)) {
    // Set administrator == 0
    rc = sqlite_exec_printf( db,
			     string( string("update users ") + 
				     string("set administrator = 0 where ") +
				     string("user = %Q;")).c_str(),
			     0, 0, &err,
			     username.c_str());
  
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }
  else if( userIsOperator(username)) {
    // Set operator == 0
    rc = sqlite_exec_printf( db,
			     string( string("update users ") + 
				     string("set operator = 0 where ") +
				     string("user = %Q;")).c_str(),
			     0, 0, &err,
			     username.c_str());
  
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }
  else if( userIsRegular(username)) {
    // Set regular == 0
    rc = sqlite_exec_printf( db,
			     string( string("update users ") + 
				     string("set regular = 0 where ") +
				     string("user = %Q;")).c_str(),
			     0, 0, &err,
			     username.c_str());
  
    if(rc != SQLITE_OK && err != 0) {
      botlog << "SQL ERROR:  " << string(err) << endl;
      free(err);
      err = 0;
      return false;
    }
  }
  else if( userIsGuest(username)) {
    // The only last case.  We can't demote a guest.  It's the lowest rank.
    return false;
  }
  return true;
}


// Admins will not be banned.
bool bot::banUser(string username) {
  int rc;
  char *err = 0;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  if( userIsAdministrator(username))
    return false;

  // Set banned status.
  rc = sqlite_exec_printf( db,
			   string( string("update users ") + 
				   string("set banned = 1 where ") +
				   string("user = %Q;")).c_str(),
			   0, 0, &err,
			   username.c_str());
  
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }
  return true;
}

bool bot::unBanUser(string username) {
  int rc;
  char *err = 0;

  // First convert the username to lowercase.
  for(unsigned int i = 0; i < username.size(); i++)
    username[i] = tolower(username[i]);

  if(! user_exists(username))
    return false;

  // Unset banned status.
  rc = sqlite_exec_printf( db,
			   string( string("update users ") + 
				   string("set banned = 0 where ") +
				   string("user = %Q;")).c_str(),
			   0, 0, &err,
			   username.c_str());
  
  if(rc != SQLITE_OK && err != 0) {
    botlog << "SQL ERROR:  " << string(err) << endl;
    free(err);
    err = 0;
    return false;
  }
  return true;
}


//////////////////////////////////////////////////////////////////////////////
// End of file
//////////////////////////////////////////////////////////////////////////////
